diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
index bf8c7c1..19ef39a 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
@@ -86,6 +86,43 @@ public function post($id, EntityInterface $entity) {
   }
 
   /**
+   * Responds to entity PUT requests.
+   *
+   * @param mixed $id
+   *   The entity ID.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity.
+   *
+   * @return \Drupal\rest\ResourceResponse
+   *   The HTTP response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
+   */
+  public function put($id, EntityInterface $entity) {
+    if (empty($id)) {
+      throw new NotFoundHttpException();
+    }
+    $definition = $this->getDefinition();
+    $original_entity = entity_load($definition['entity_type'], $id);
+    // We don't support creating entities with PUT, so we throw an error if
+    // there is no existing entity.
+    if ($original_entity == FALSE) {
+      throw new NotFoundHttpException();
+    }
+    $info = $entity->entityInfo();
+    // Make sure that the entity ID is the one provided in the URL.
+    $entity->{$info['entity_keys']['id']} = $id;
+    try {
+      $entity->save();
+      // Update responses have an empty body.
+      return new ResourceResponse(NULL, 204);
+    }
+    catch (EntityStorageException $e) {
+      throw new HttpException(500, 'Internal Server Error', $e);
+    }
+  }
+
+  /**
    * Responds to entity PATCH requests.
    *
    * @param mixed $id
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
index eed2b2f..83729aa 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
@@ -96,4 +96,77 @@ public function testPatchUpdate() {
     $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PATCH', $serialized, 'application/vnd.drupal.ld+json');
     $this->assertResponse(404);
   }
+
+  /**
+   * Tests several valid and invalid PUT update requests on test entities.
+   */
+  public function testPutUpdate() {
+    $serializer = drupal_container()->get('serializer');
+    // @todo once EntityNG is implemented for other entity types test all other
+    // entity types here as well.
+    $entity_type = 'entity_test';
+
+    $this->enableService('entity:' . $entity_type);
+    // Create a user account that has the required permissions to create
+    // resources via the web API.
+    $account = $this->drupalCreateUser(array('restful put entity:' . $entity_type));
+    $this->drupalLogin($account);
+
+    // Create an entity and save it to the database.
+    $entity = $this->entityCreate($entity_type);
+    $entity->save();
+
+    // Create a second entity that will overwrite the original.
+    $update_values = $this->entityValues($entity_type);
+    $update_entity = entity_create($entity_type, $update_values);
+    // Copy the identifier properties over from the original.
+    $update_entity->uuid->value = $entity->uuid();
+    $update_entity->id->value = $entity->id();
+
+    $serialized = $serializer->serialize($update_entity, 'drupal_jsonld');
+    // Update the entity over the web API.
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(204);
+
+    // Re-load the updated entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    // @todo Don't check the user reference field for now until deserialization
+    // for entity references is implemented.
+    unset($update_values['user_id']);
+    foreach ($update_values as $property => $value) {
+      $update_value = $update_entity->{$property}->value;
+      $stored_value = $entity->{$property}->value;
+      $this->assertEqual($stored_value, $update_value, 'Updated property ' . $property . ' expected: ' . $update_value . ', actual: ' . $stored_value);
+    }
+
+    // Try to delete a property.
+    unset($update_entity->field_test_text);
+    $serialized = $serializer->serialize($update_entity, 'drupal_jsonld');
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(204);
+
+    // Re-load the updated entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    $this->assertTrue($entity->field_test_text->isEmpty(), 'Property has been deleted.');
+
+    // Try to create an entity with ID 9999.
+    $this->httpRequest('entity/' . $entity_type . '/9999', 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(404);
+    $loaded_entity = entity_load($entity_type, 9999, TRUE);
+    $this->assertFalse($loaded_entity, 'Entity 9999 was not created.');
+
+    // Try to update an entity without proper permissions.
+    $this->drupalLogout();
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+
+    // Try to update a resource which is not web API enabled.
+    $this->enableService(FALSE);
+    // Reset cURL here because it is confused from our previously used cURL
+    // options.
+    unset($this->curlHandle);
+    $this->drupalLogin($account);
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(404);
+  }
 }
