diff --git a/swift/container/server.py b/swift/container/server.py
index c8d7647aa6..db9ac02910 100644
--- a/swift/container/server.py
+++ b/swift/container/server.py
@@ -155,6 +155,8 @@ class ContainerController(BaseStorageServer):
                 conf['auto_create_account_prefix']
         else:
             self.auto_create_account_prefix = AUTO_CREATE_ACCOUNT_PREFIX
+        self.shards_account_prefix = (
+            self.auto_create_account_prefix + 'shards_')
         if config_true_value(conf.get('allow_versions', 'f')):
             self.save_headers.append('x-versions-location')
         if 'allow_versions' in conf:
@@ -375,14 +377,12 @@ class ContainerController(BaseStorageServer):
         # auto create accounts)
         obj_policy_index = self.get_and_validate_policy_index(req) or 0
         broker = self._get_container_broker(drive, part, account, container)
-        if account.startswith(self.auto_create_account_prefix) and obj and \
-                not os.path.exists(broker.db_file):
-            try:
-                broker.initialize(req_timestamp.internal, obj_policy_index)
-            except DatabaseAlreadyExists:
-                pass
-        if not os.path.exists(broker.db_file):
+        if obj:
+            self._maybe_autocreate(broker, req_timestamp, account,
+                                   obj_policy_index, req)
+        elif not os.path.exists(broker.db_file):
             return HTTPNotFound()
+
         if obj:     # delete object
             # redirect if a shard range exists for the object name
             redirect = self._redirect_to_shard(req, broker, obj)
@@ -449,11 +449,25 @@ class ContainerController(BaseStorageServer):
             broker.update_status_changed_at(timestamp)
         return recreated
 
+    def _should_autocreate(self, account, req):
+        auto_create_header = req.headers.get('X-Backend-Auto-Create')
+        if auto_create_header:
+            # If the caller included an explicit X-Backend-Auto-Create header,
+            # assume they know the behavior they want
+            return config_true_value(auto_create_header)
+        if account.startswith(self.shards_account_prefix):
+            # we have to specical case this subset of the
+            # auto_create_account_prefix because we don't want the updater
+            # accidently auto-creating shards; only the sharder creates
+            # shards and it will explicitly tell the server to do so
+            return False
+        return account.startswith(self.auto_create_account_prefix)
+
     def _maybe_autocreate(self, broker, req_timestamp, account,
-                          policy_index):
+                          policy_index, req):
         created = False
-        if account.startswith(self.auto_create_account_prefix) and \
-                not os.path.exists(broker.db_file):
+        should_autocreate = self._should_autocreate(account, req)
+        if should_autocreate and not os.path.exists(broker.db_file):
             if policy_index is None:
                 raise HTTPBadRequest(
                     'X-Backend-Storage-Policy-Index header is required')
@@ -506,8 +520,8 @@ class ContainerController(BaseStorageServer):
             # obj put expects the policy_index header, default is for
             # legacy support during upgrade.
             obj_policy_index = requested_policy_index or 0
-            self._maybe_autocreate(broker, req_timestamp, account,
-                                   obj_policy_index)
+            self._maybe_autocreate(
+                broker, req_timestamp, account, obj_policy_index, req)
             # redirect if a shard exists for this object name
             response = self._redirect_to_shard(req, broker, obj)
             if response:
@@ -531,8 +545,8 @@ class ContainerController(BaseStorageServer):
                                 for sr in json.loads(req.body)]
             except (ValueError, KeyError, TypeError) as err:
                 return HTTPBadRequest('Invalid body: %r' % err)
-            created = self._maybe_autocreate(broker, req_timestamp, account,
-                                             requested_policy_index)
+            created = self._maybe_autocreate(
+                broker, req_timestamp, account, requested_policy_index, req)
             self._update_metadata(req, broker, req_timestamp, 'PUT')
             if shard_ranges:
                 # TODO: consider writing the shard ranges into the pending
@@ -805,7 +819,7 @@ class ContainerController(BaseStorageServer):
         requested_policy_index = self.get_and_validate_policy_index(req)
         broker = self._get_container_broker(drive, part, account, container)
         self._maybe_autocreate(broker, req_timestamp, account,
-                               requested_policy_index)
+                               requested_policy_index, req)
         try:
             objs = json.load(req.environ['wsgi.input'])
         except ValueError as err:
diff --git a/swift/container/sharder.py b/swift/container/sharder.py
index d9aa7c66de..d0652143e8 100644
--- a/swift/container/sharder.py
+++ b/swift/container/sharder.py
@@ -1148,7 +1148,8 @@ class ContainerSharder(ContainerReplicator):
                 'X-Backend-Storage-Policy-Index': broker.storage_policy_index,
                 'X-Container-Sysmeta-Shard-Quoted-Root': quote(
                     broker.root_path),
-                'X-Container-Sysmeta-Sharding': True}
+                'X-Container-Sysmeta-Sharding': 'True',
+                'X-Backend-Auto-Create': 'True'}
             # NB: we *used* to send along
             #    'X-Container-Sysmeta-Shard-Root': broker.root_path
             # but that isn't safe for container names with nulls or newlines
diff --git a/test/unit/container/test_server.py b/test/unit/container/test_server.py
index fc55ff05d8..4fd1fcf2e3 100644
--- a/test/unit/container/test_server.py
+++ b/test/unit/container/test_server.py
@@ -2380,15 +2380,17 @@ class TestContainerController(unittest.TestCase):
                    'X-Container-Sysmeta-Test': 'set',
                    'X-Container-Meta-Test': 'persisted'}
 
-        # PUT shard range to non-existent container with non-autocreate prefix
-        req = Request.blank('/sda1/p/a/c', method='PUT', headers=headers,
-                            body=json.dumps([dict(shard_range)]))
+        # PUT shard range to non-existent container without autocreate flag
+        req = Request.blank(
+            '/sda1/p/.shards_a/shard_c', method='PUT', headers=headers,
+            body=json.dumps([dict(shard_range)]))
         resp = req.get_response(self.controller)
         self.assertEqual(404, resp.status_int)
 
-        # PUT shard range to non-existent container with autocreate prefix,
+        # PUT shard range to non-existent container with autocreate flag,
         # missing storage policy
         headers['X-Timestamp'] = next(ts_iter).internal
+        headers['X-Backend-Auto-Create'] = 't'
         req = Request.blank(
             '/sda1/p/.shards_a/shard_c', method='PUT', headers=headers,
             body=json.dumps([dict(shard_range)]))
@@ -2397,7 +2399,7 @@ class TestContainerController(unittest.TestCase):
         self.assertIn(b'X-Backend-Storage-Policy-Index header is required',
                       resp.body)
 
-        # PUT shard range to non-existent container with autocreate prefix
+        # PUT shard range to non-existent container with autocreate flag
         headers['X-Timestamp'] = next(ts_iter).internal
         policy_index = random.choice(POLICIES).idx
         headers['X-Backend-Storage-Policy-Index'] = str(policy_index)
@@ -2407,7 +2409,7 @@ class TestContainerController(unittest.TestCase):
         resp = req.get_response(self.controller)
         self.assertEqual(201, resp.status_int)
 
-        # repeat PUT of shard range to autocreated container - 204 response
+        # repeat PUT of shard range to autocreated container - 202 response
         headers['X-Timestamp'] = next(ts_iter).internal
         headers.pop('X-Backend-Storage-Policy-Index')  # no longer required
         req = Request.blank(
@@ -2416,7 +2418,7 @@ class TestContainerController(unittest.TestCase):
         resp = req.get_response(self.controller)
         self.assertEqual(202, resp.status_int)
 
-        # regular PUT to autocreated container - 204 response
+        # regular PUT to autocreated container - 202 response
         headers['X-Timestamp'] = next(ts_iter).internal
         req = Request.blank(
             '/sda1/p/.shards_a/shard_c', method='PUT',
@@ -4649,61 +4651,53 @@ class TestContainerController(unittest.TestCase):
                              "%d on param %s" % (resp.status_int, param))
 
     def test_put_auto_create(self):
-        headers = {'x-timestamp': Timestamp(1).internal,
-                   'x-size': '0',
-                   'x-content-type': 'text/plain',
-                   'x-etag': 'd41d8cd98f00b204e9800998ecf8427e'}
+        def do_test(expected_status, path, extra_headers=None, body=None):
+            headers = {'x-timestamp': Timestamp(1).internal,
+                       'x-size': '0',
+                       'x-content-type': 'text/plain',
+                       'x-etag': 'd41d8cd98f00b204e9800998ecf8427e'}
+            if extra_headers:
+                headers.update(extra_headers)
+            req = Request.blank('/sda1/p/' + path,
+                                environ={'REQUEST_METHOD': 'PUT'},
+                                headers=headers, body=body)
+            resp = req.get_response(self.controller)
+            self.assertEqual(resp.status_int, expected_status)
 
-        req = Request.blank('/sda1/p/a/c/o',
-                            environ={'REQUEST_METHOD': 'PUT'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
+        do_test(404, 'a/c/o')
+        do_test(404, '.a/c/o', {'X-Backend-Auto-Create': 'no'})
+        do_test(201, '.a/c/o')
+        do_test(404, 'a/.c/o')
+        do_test(404, 'a/c/.o')
+        do_test(201, 'a/c/o', {'X-Backend-Auto-Create': 'yes'})
 
-        req = Request.blank('/sda1/p/.a/c/o',
-                            environ={'REQUEST_METHOD': 'PUT'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 201)
-
-        req = Request.blank('/sda1/p/a/.c/o',
-                            environ={'REQUEST_METHOD': 'PUT'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
-
-        req = Request.blank('/sda1/p/a/c/.o',
-                            environ={'REQUEST_METHOD': 'PUT'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
+        do_test(404, '.shards_a/c/o')
+        create_shard_headers = {
+            'X-Backend-Record-Type': 'shard',
+            'X-Backend-Storage-Policy-Index': '0'}
+        do_test(404, '.shards_a/c', create_shard_headers, '[]')
+        create_shard_headers['X-Backend-Auto-Create'] = 't'
+        do_test(201, '.shards_a/c', create_shard_headers, '[]')
 
     def test_delete_auto_create(self):
-        headers = {'x-timestamp': Timestamp(1).internal}
+        def do_test(expected_status, path, extra_headers=None):
+            headers = {'x-timestamp': Timestamp(1).internal}
+            if extra_headers:
+                headers.update(extra_headers)
+            req = Request.blank('/sda1/p/' + path,
+                                environ={'REQUEST_METHOD': 'DELETE'},
+                                headers=headers)
+            resp = req.get_response(self.controller)
+            self.assertEqual(resp.status_int, expected_status)
 
-        req = Request.blank('/sda1/p/a/c/o',
-                            environ={'REQUEST_METHOD': 'DELETE'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
-
-        req = Request.blank('/sda1/p/.a/c/o',
-                            environ={'REQUEST_METHOD': 'DELETE'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 204)
-
-        req = Request.blank('/sda1/p/a/.c/o',
-                            environ={'REQUEST_METHOD': 'DELETE'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
-
-        req = Request.blank('/sda1/p/a/.c/.o',
-                            environ={'REQUEST_METHOD': 'DELETE'},
-                            headers=dict(headers))
-        resp = req.get_response(self.controller)
-        self.assertEqual(resp.status_int, 404)
+        do_test(404, 'a/c/o')
+        do_test(404, '.a/c/o', {'X-Backend-Auto-Create': 'false'})
+        do_test(204, '.a/c/o')
+        do_test(404, 'a/.c/o')
+        do_test(404, 'a/.c/.o')
+        do_test(404, '.shards_a/c/o')
+        do_test(204, 'a/c/o', {'X-Backend-Auto-Create': 'true'})
+        do_test(204, '.shards_a/c/o', {'X-Backend-Auto-Create': 'true'})
 
     def test_content_type_on_HEAD(self):
         Request.blank('/sda1/p/a/o',