diff --git a/search_api.views.inc b/search_api.views.inc
index 0b23606..90ba698 100644
--- a/search_api.views.inc
+++ b/search_api.views.inc
@@ -51,17 +51,19 @@ function search_api_views_data() {
       );
 
       // Add suitable handlers for all indexed fields.
-      foreach ($index->getFields() as $field_id => $field) {
+      foreach ($index->getFields(TRUE) as $field_id => $field) {
         $field_alias = _search_api_views_find_field_alias($field_id, $table);
         $field_definition = _search_api_views_get_handlers($field);
         // The field handler has to be extra, since it is a) determined by the
         // field's underlying property and b) needs a different "real field"
         // set.
-        $field_handler = _search_api_views_get_field_handler_for_property($field->getDataDefinition(), $field->getPropertyPath());
-        if ($field_handler) {
-          $field_definition['field'] = $field_handler;
-          $field_definition['field']['real field'] = $field->getCombinedPropertyPath();
-          $field_definition['field']['click sortable'] = TRUE;
+        if ($field->getPropertyPath()) {
+          $field_handler = _search_api_views_get_field_handler_for_property($field->getDataDefinition(), $field->getPropertyPath());
+          if ($field_handler) {
+            $field_definition['field'] = $field_handler;
+            $field_definition['field']['real field'] = $field->getCombinedPropertyPath();
+            $field_definition['field']['click sortable'] = TRUE;
+          }
         }
         if ($field_definition) {
           $field_label = $field->getLabel();
diff --git a/src/Backend/BackendPluginBase.php b/src/Backend/BackendPluginBase.php
index 74e03d5..639c9ad 100644
--- a/src/Backend/BackendPluginBase.php
+++ b/src/Backend/BackendPluginBase.php
@@ -145,6 +145,13 @@ public function preDelete() {
   /**
    * {@inheritdoc}
    */
+  public function getBackendDefinedFields(IndexInterface $index) {
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function addIndex(IndexInterface $index) {}
 
   /**
diff --git a/src/Backend/BackendSpecificInterface.php b/src/Backend/BackendSpecificInterface.php
index b0ef606..014dae6 100644
--- a/src/Backend/BackendSpecificInterface.php
+++ b/src/Backend/BackendSpecificInterface.php
@@ -90,6 +90,28 @@ public function supportsDataType($type);
   public function getDiscouragedProcessors();
 
   /**
+   * Provides information on additional fields made available by the backend.
+   *
+   * If a backend indexes additional data with items and wants to make this
+   * available as fixed fields on the index (e.g., to be used with Views), it
+   * can implement this method to facilitate this.
+   *
+   * Fields returned here are expected to work correctly with this server when
+   * used in query conditions, sorts or similar places.
+   *
+   * @param \Drupal\search_api\IndexInterface $index
+   *   The index for which fields are being determined.
+   *
+   * @return \Drupal\search_api\Item\FieldInterface[]
+   *   An array of additional fields that are available for this index, keyed by
+   *   their field IDs. The field IDs should always start with "search_api_"
+   *   (avoiding the special field IDs defined by
+   *   \Drupal\search_api\Query\QueryInterface::sort()) to avoid conflicts with
+   *   user-defined fields.
+   */
+  public function getBackendDefinedFields(IndexInterface $index);
+
+  /**
    * Adds a new index to this server.
    *
    * If the index was already added to the server, the object should treat this
diff --git a/src/Entity/Index.php b/src/Entity/Index.php
index fc50125..f870e91 100644
--- a/src/Entity/Index.php
+++ b/src/Entity/Index.php
@@ -771,19 +771,18 @@ public function removeField($field_id) {
   /**
    * {@inheritdoc}
    */
-  public function getFields() {
-    // ::$fieldInstances is already filled with fields, so keep on using those.
-    if ($this->fieldInstances !== NULL) {
-      return $this->fieldInstances;
+  public function getFields($include_server_defined = FALSE) {
+    if (!isset($this->fieldInstances)) {
+      $this->fieldInstances = array();
+      foreach ($this->field_settings as $key => $field_info) {
+        $this->fieldInstances[$key] = Utility::createField($this, $key, $field_info);
+      }
     }
 
-    $fields = array();
-    foreach ($this->field_settings as $key => $field_info) {
-      $fields[$key] = Utility::createField($this, $key, $field_info);
+    $fields = $this->fieldInstances;
+    if ($include_server_defined && $this->hasValidServer()) {
+      $fields += $this->getServerInstance()->getBackendDefinedFields($this);
     }
-
-    $this->fieldInstances = $fields;
-
     return $fields;
   }
 
diff --git a/src/Entity/Server.php b/src/Entity/Server.php
index 13ef7fb..0bcbcdb 100644
--- a/src/Entity/Server.php
+++ b/src/Entity/Server.php
@@ -188,14 +188,30 @@ public function supportsFeature($feature) {
    * {@inheritdoc}
    */
   public function supportsDataType($type) {
-    return $this->hasValidBackend() ? $this->getBackend()->supportsDataType($type) : FALSE;
+    if ($this->hasValidBackend()) {
+      return $this->getBackend()->supportsDataType($type);
+    }
+    return FALSE;
   }
 
   /**
    * {@inheritdoc}
    */
   public function getDiscouragedProcessors() {
-    return $this->getBackend()->getDiscouragedProcessors();
+    if ($this->hasValidBackend()) {
+      return $this->getBackend()->getDiscouragedProcessors();
+    }
+    return array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBackendDefinedFields(IndexInterface $index) {
+    if ($this->hasValidBackend()) {
+      return $this->getBackend()->getBackendDefinedFields($index);
+    }
+    return array();
   }
 
   /**
diff --git a/src/IndexInterface.php b/src/IndexInterface.php
index 41b5276..2d510a5 100644
--- a/src/IndexInterface.php
+++ b/src/IndexInterface.php
@@ -493,10 +493,15 @@ public function removeField($field_id);
   /**
    * Returns a list of all indexed fields of this index.
    *
+   * @param bool $include_server_defined
+   *   (optional) If TRUE, also include special fields defined by the server
+   *   backend. For more information, see
+   *   \Drupal\search_api\Backend\BackendSpecificInterface::getBackendDefinedFields().
+   *
    * @return \Drupal\search_api\Item\FieldInterface[]
    *   An array of all indexed fields for this index, keyed by field identifier.
    */
-  public function getFields();
+  public function getFields($include_server_defined = FALSE);
 
   /**
    * Returns a field from this index.
diff --git a/src/Tests/IntegrationTest.php b/src/Tests/IntegrationTest.php
index 8a21e83..45ffbad 100644
--- a/src/Tests/IntegrationTest.php
+++ b/src/Tests/IntegrationTest.php
@@ -962,15 +962,15 @@ protected function changeProcessorFieldBoost() {
 
     // Change the boost of the field.
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->drupalPostForm(NULL, array('fields[search_api_url][boost]' => '8.0'), $this->t('Save changes'));
+    $this->drupalPostForm(NULL, array('fields[url][boost]' => '8.0'), $this->t('Save changes'));
     $this->assertText('The changes were successfully saved.');
-    $this->assertOptionSelected('edit-fields-search-api-url-boost', '8.0', 'Boost is correctly saved.');
+    $this->assertOptionSelected('edit-fields-url-boost', '8.0', 'Boost is correctly saved.');
 
     // Change the type of the field.
     $this->drupalGet($this->getIndexPath('fields'));
-    $this->drupalPostForm(NULL, array('fields[search_api_url][type]' => 'text'), $this->t('Save changes'));
+    $this->drupalPostForm(NULL, array('fields[url][type]' => 'text'), $this->t('Save changes'));
     $this->assertText('The changes were successfully saved.');
-    $this->assertOptionSelected('edit-fields-search-api-url-type', 'text', 'Type is correctly saved.');
+    $this->assertOptionSelected('edit-fields-url-type', 'text', 'Type is correctly saved.');
   }
 
   /**
diff --git a/src/UnsavedIndexConfiguration.php b/src/UnsavedIndexConfiguration.php
index 82b9963..5b6e4a4 100644
--- a/src/UnsavedIndexConfiguration.php
+++ b/src/UnsavedIndexConfiguration.php
@@ -323,8 +323,8 @@ public function postprocessSearchResults(ResultSetInterface $results) {
   /**
    * {@inheritdoc}
    */
-  public function getFields() {
-    return $this->entity->getFields();
+  public function getFields($include_server_defined = FALSE) {
+    return $this->entity->getFields($include_server_defined);
   }
 
   /**
diff --git a/src/Utility.php b/src/Utility.php
index bbcfd6d..afe135d 100644
--- a/src/Utility.php
+++ b/src/Utility.php
@@ -406,9 +406,8 @@ public static function splitPropertyPath($property_path, $separate_last = TRUE,
   /**
    * Determines whether a field ID is reserved for special use.
    *
-   * This is the case for the "magic" pseudo-fields documented in
-   * \Drupal\search_api\Query\QueryInterface for use in queries, like
-   * "search_api_id".
+   * We define all field IDs starting with "search_api_" as reserved, to be safe
+   * for future additions (and from clashing with backend-defined fields).
    *
    * @param string $field_id
    *   The field ID.
@@ -417,13 +416,7 @@ public static function splitPropertyPath($property_path, $separate_last = TRUE,
    *   TRUE if the field ID is reserved, FALSE if it can be used normally.
    */
   public static function isFieldIdReserved($field_id) {
-    $reserved_ids = array_flip(array(
-      'search_api_id',
-      'search_api_datasource',
-      'search_api_language',
-      'search_api_relevance',
-    ));
-    return isset($reserved_ids[$field_id]);
+    return substr($field_id, 0, 11) == 'search_api_';
   }
 
   /**
@@ -658,12 +651,20 @@ public static function createFieldFromProperty(IndexInterface $index, DataDefini
   public static function getNewFieldId(IndexInterface $index, $property_path) {
     list(, $suggested_id) = static::splitPropertyPath($property_path);
 
+    // Avoid clashes with reserved IDs by removing the reserved "search_api_"
+    // from our suggested ID.
+    $suggested_id = str_replace('search_api_', '', $suggested_id);
+
     $field_id = $suggested_id;
     $i = 0;
     while ($index->getField($field_id)) {
       $field_id = $suggested_id . '_' . ++$i;
     }
 
+    while (static::isFieldIdReserved($field_id)) {
+      $field_id = '_' . $field_id;
+    }
+
     return $field_id;
   }
 
diff --git a/tests/src/Kernel/Processor/ContentAccessTest.php b/tests/src/Kernel/Processor/ContentAccessTest.php
index 7ee79a5..52eaa87 100644
--- a/tests/src/Kernel/Processor/ContentAccessTest.php
+++ b/tests/src/Kernel/Processor/ContentAccessTest.php
@@ -247,7 +247,7 @@ public function testContentAccessAll() {
     }
 
     foreach ($items as $item) {
-      $this->assertEquals(array('node_access__all'), $item->getField('search_api_node_grants')->getValues());
+      $this->assertEquals(array('node_access__all'), $item->getField('node_grants')->getValues());
     }
   }
 
@@ -272,7 +272,7 @@ public function testContentAccessWithNodeGrants() {
     }
 
     foreach ($items as $item) {
-      $this->assertEquals(array('node_access_search_api_test:0'), $item->getField('search_api_node_grants')->getValues());
+      $this->assertEquals(array('node_access_search_api_test:0'), $item->getField('node_grants')->getValues());
     }
   }
 
diff --git a/tests/src/Unit/Plugin/Processor/AddURLTest.php b/tests/src/Unit/Plugin/Processor/AddURLTest.php
index 7da06c8..f8a37d5 100644
--- a/tests/src/Unit/Plugin/Processor/AddURLTest.php
+++ b/tests/src/Unit/Plugin/Processor/AddURLTest.php
@@ -98,7 +98,7 @@ public function testAddFieldValues() {
     }
 
     // Check the valid item.
-    $field = $items[$this->itemIds[0]]->getField('search_api_url');
+    $field = $items[$this->itemIds[0]]->getField('url');
     $this->assertEquals(array('http://www.example.com/node/example'), $field->getValues(), 'Valid URL added as value to the field.');
 
     // Check that no other fields were changed.
@@ -106,7 +106,7 @@ public function testAddFieldValues() {
     $this->assertEquals($body_value, $field->getValues(), 'Body field was not changed.');
 
     // Check the second item to be sure that all are processed.
-    $field = $items[$this->itemIds[1]]->getField('search_api_url');
+    $field = $items[$this->itemIds[1]]->getField('url');
     $this->assertEquals(array('http://www.example.com/node/example'), $field->getValues(), 'Valid URL added as value to the field in the second item.');
   }
 
