S3 Bucket URL Formats and Path vs Virtual-Hosted Style
Path-style puts buckets in the URL path; virtual-hosted puts them in the subdomain instead.
Sponsored by Archil

Amazon S3 gives you two ways to write the same URL, and they both point at the exact same object. The only thing that changes is where the bucket name lives.
Path-style puts the bucket name in the URL path, right after the domain, like a folder you're walking into: s3.us-east-2.amazonaws.com/my-data-exports/reports/2024/january.csv. Virtual-hosted-style promotes that same bucket name up into the subdomain instead: my-data-exports.s3.us-east-2.amazonaws.com/reports/2024/january.csv. Everything after the bucket name, the object key, stays identical in both versions. Only the bucket's address changes.
There's also a legacy regionless path-style format still floating around in older codebases, one with no region in it. It implicitly targets us-east-1, and it's a bad idea for anything new you're building. It remains a fossil, not a pattern to copy.
That's the whole structural difference: bucket name in the path, or bucket name in the subdomain. Everything else in this guide, the routing behavior, the SSL quirks, the security posture, the ecosystem split, follows from that one choice.
Why bucket-name-in-subdomain gives AWS more routing flexibility than bucket-name-in-path
If the bucket name sits in the path, every request for every bucket in a region funnels through the same shared domain. AWS has no way to tell bucket A's traffic from bucket B's traffic using DNS alone, because DNS only sees the domain, not the path sitting after it. One set of IP addresses handles everything, no matter how different those buckets' needs might be.
Moving the bucket name into the subdomain gives each bucket its own DNS entry. That sounds small, but it isn't. It means AWS can route traffic differently depending on which bucket is being hit, apply security controls bucket by bucket, and contain the damage from any incident to a single bucket's subdomain instead of the whole regional pool. Think of it like the difference between an apartment building with one shared mailbox and a building where every unit has its own: the second one lets the mail carrier (or the security guard) deal with problems one door at a time.
AWS's own deprecation announcement names this as the main reason for the push: with billions of buckets eventually spread across dozens of regions, routing every single request through a small, shared set of endpoints stops scaling. The second reason AWS gives is more forward-looking. Features already in development depend on each bucket having its own unique subdomain, so the migration unlocks those features for customers the moment they ship.
The deprecation timeline, the delay, and what is required today
AWS announced in May 2019 that it planned to retire path-style support for new buckets. The original cutoff was September 30, 2020. After that date, AWS said, path-style requests for newly created buckets would stop working.
Then, in September 2020, AWS walked it back. Customer feedback pushed the company to delay the deprecation indefinitely on September 23, 2020, just one week before the original deadline. Buckets created on or before September 30, 2020 keep path-style support. Buckets created after that date are expected to use virtual-hosted-style, though path-style access continues to be supported more broadly today, with virtual-hosted-style standing as AWS's stated recommendation rather than a hard requirement.
AWS also committed to giving at least a full year's notice before any future deprecation of path-style for new buckets, and tied that promise to a separate piece of work: fixing virtual-hosted-style support for bucket names with dots in them (more on that next).
Here's where this actually bites developers. A Terraform module humming along for two years gets copied into a new AWS account, pointed at a freshly created bucket, and throws access errors on day one. Nothing about the code changed. The bucket just happens to postdate September 2020, and the URL format baked into the config was never updated to match. The fix is usually a one-line config change, but finding that one line after assuming it's a permissions problem can eat an afternoon.
One more detail that matters regardless of which format you use: always include the region in the URL. Leaving it out adds redirect latency, and for any bucket created after September 2020 outside us-east-1, it breaks the request.
Why dotted bucket names are the one case that keeps path-style from being fully replaceable
Bucket names with dots in them break virtual-hosted-style under SSL, and this single fact is the main reason path-style can't be fully retired yet. The problem isn't routing. It's certificates.
AWS's SSL wildcard certificate covers one level of subdomain. A bucket name with a dot in it turns into an extra subdomain level, and the certificate simply doesn't extend that far. AWS documentation states this directly: the wildcard certificate matches only bucket names without dots, and the only workarounds are dropping down to plain HTTP or writing custom certificate-verification logic.
Take a bucket literally named my.data.bucket. Under virtual-hosted-style, it resolves to my.data.bucket.s3.region.amazonaws.com, a five-level subdomain. The wildcard certificate, built for *.s3.region.amazonaws.com, covers one level of wildcard, not three extra ones stacked on top. The mismatch triggers a certificate validation error, the kind that looks like a security problem and is actually a naming problem.
AWS has acknowledged the gap and said it's working on virtual-hosted-style support for dotted bucket names, and it has repeated the promise of a full year's notice before deprecating path-style for new buckets once that support ships. Deprecation for new buckets has already moved forward in part, but the dotted-name fix hasn't shipped yet. Until it does, any codebase with a dotted bucket name has a real technical reason to stay on path-style. That's a certificate that doesn't exist yet, not a team dragging its feet on an upgrade.
Any system that wraps itself around S3 buckets, whether that's a filesystem mount, a data pipeline, or a storage layer behind an AI agent, inherits this exact constraint. The SSL failure with dots forces every layer built on top of S3 to either fall back on path-style for those buckets or write its own certificate-handling logic, a hard limit that reaches well beyond someone typing URLs by hand.
A security risk specific to path-style routing that the deprecation debate usually skips
Most of the conversation around deprecating path-style focuses on performance and DNS routing. There's a security argument that gets left out almost entirely, and it's a good one: path-style is structurally open to path-traversal attacks in a way virtual-hosted-style simply isn't.
The vulnerability comes straight from the structure described earlier. Because the bucket name sits in the URL path alongside the object key, a router handling path-traversal logic can, if poorly built, be tricked into crossing bucket boundaries it was never supposed to cross. The bucket name is just another path segment, and path segments are exactly what traversal attacks manipulate.
Virtual-hosted-style sidesteps the problem by construction. The bucket name lives in the DNS hostname, and no URL path-traversal technique reaches into the hostname. The thing an attacker would need to manipulate isn't part of the path, so there's no equivalent attack surface to exploit.
This isn't an argument AWS leads with in its own deprecation materials, but it holds just as much weight on S3-compatible systems outside AWS as it does inside it. Any system mounting S3 or S3-compatible storage as a filesystem benefits here too. One such tool, which exposes object storage as native filesystem mounts, is an example of something that gains real ground from virtual-hosted-style's per-bucket DNS routing: bucket-level caching, bucket-level security policy, and performance tuning, all without funneling every request through one shared regional chokepoint.
Why S3-compatible storage systems keep path-style relevant across the non-AWS ecosystem
AWS wants everyone on virtual-hosted-style. The rest of the S3-compatible world didn't get the memo, and for good structural reasons.
The most common reason a developer runs into path-style today is an S3-compatible storage system that defaults to path-style or requires it, because that system never built out per-bucket DNS subdomains. Ceph, SeaweedFS, LocalStack, and NetApp's StorageGRID all fall into this bucket (so to speak).
SeaweedFS is a clean example. It uses path-style addressing and doesn't provision per-bucket DNS subdomains at all, so every connection needs s3.use_path_style = true set explicitly. That matters beyond SeaweedFS itself, because Kubeflow Pipelines officially adopted SeaweedFS as its default storage backend. This path-style configuration is now the standard setup inside a widely deployed machine learning pipeline.
StorageGRID documents path-style as its default whenever a client connects by IP address instead of a domain name, and virtual-hosted-style requires explicit DNS configuration to work. Path-style isn't disappearing from on-premises deployments any time soon. Ceph's RADOS Gateway supports both formats, but switching on virtual-hosted-style means configuring either rgw_dns_name or a zonegroup hostname. Path-style works with nothing more than a bare IP address, and that is why it stays the path of least resistance for self-hosted storage.
LocalStack, the AWS emulator most developers run locally before touching production, defaults to virtual-hosted-style through its s3.localhost.localstack.cloud subdomain. That subdomain doesn't always resolve cleanly on every machine, so forcePathStyle: true ends up getting set anyway. Local development environments end up needing that flag regardless of what production uses, which creates its own small trap: a developer's laptop and the production server can disagree on URL format for reasons that have nothing to do with the application code.
The zero-egress-fee alternatives in this space, Cloudflare R2 and Wasabi among them, ask for a custom endpoint URL plus an explicit URL style. Path-style paired with an endpoint override is the typical setup there too.
Put it together and there's a real split. AWS is pushing everyone toward virtual-hosted-style. The broader S3-compatible ecosystem defaults to path-style. Any codebase meant to run across more than one of these environments has to support both formats and flip between them with a configuration flag. That flag is a required feature of anything built to be cloud-agnostic.
How to configure each format in practice
Switching between path-style and virtual-hosted-style comes down to one setting. The name of that setting, and where you set it, changes depending on the SDK and language.
In the AWS SDK for Java 2.x, virtual-hosted-style is the default behavior. Turning on path-style means passing S3Configuration.builder().pathStyleAccessEnabled(true) as the serviceConfiguration on your client builder. Starting with SDK 2.18, there's a shortcut: forcePathStyle(true) sits directly on the client builder next to endpointOverride.
The AWS SDK v3 for JavaScript handles it with forcePathStyle: true set in the S3Client constructor, right alongside the custom endpoint URL you're pointing it at.
For Python, using boto3 (AWS SDK v2), the setting is config=Config(s3={'addressing_style': 'path'}), passed into boto3.client().
The AWS CLI handles it globally: aws configure set default.s3.addressing_style virtual sets virtual-hosted-style everywhere. Swapping virtual for path sets path-style instead.
The decision rule is simple enough to keep in your head. If the endpoint is AWS, the bucket name has no dots, and the bucket was created after September 2020, use virtual-hosted-style, which is the default anyway. If the endpoint is a non-AWS S3-compatible system, or the bucket name has a dot anywhere in it, set path-style explicitly and supply a custom endpoint URL.
Always include the region, in either format. Skipping it adds redirect latency, and it breaks outright for any bucket created after September 2020 sitting outside us-east-1.
A 403 error caused by a path-style URL hitting a bucket created after the cutoff looks exactly like an IAM permissions failure. The error message won't tell you which one it is. Check the URL format before spending an hour rewriting bucket policies that were never the problem.
How the URL format question lands in AI training pipelines
In an AI training pipeline, URL addressing style is the first configuration decision that decides whether a storage backend can participate. Get it wrong, and the failure happens before the pipeline ever reaches the training data, not partway through some multi-hour transfer.
Every S3 request carries a fixed cost before a single byte moves: connection setup, a network round-trip, whatever internal operations S3 runs on its end. That cost applies no matter how big or small the object is. An incorrectly configured URL style doesn't just slow that process down. It produces redirect errors or outright failures before any of that fixed overhead even gets a chance to run.
The stakes are higher here than in a one-off script failing on a developer's laptop. GPUs chew through data orders of magnitude faster than most storage systems can hand it over. When storage latency outpaces GPU processing time, expensive compute sits there doing nothing, waiting. Across a distributed training job running on many nodes at once, that idle time doesn't just add up. It compounds, node by node, epoch by epoch.
S3 API compatibility is close to universal across the AI infrastructure market at this point. Most AI frameworks, data pipelines, and cloud-native applications assume an S3-shaped API underneath them. That makes getting the URL-style configuration right a prerequisite for the whole system to function. Any system mounting storage as a filesystem for training workloads, Archil among them, inherits all of the same path-style and virtual-hosted-style tradeoffs this guide has walked through, bucket naming, SSL behavior, and routing included.
Treat this as infrastructure, not an afterthought bolted onto a training script. The two URL formats look like a cosmetic difference in where a bucket name sits. Everything that follows from that placement, routing granularity, certificate behavior, security exposure, and whether a GPU cluster sits idle or stays fed, traces back to that one structural choice.
This article is sponsored by Archil.
Sources
- Virtual hosting of general purpose buckets - Amazon Simple Storage Service
- AWS S3 URL Formats Explained — Path Style vs Virtual Hosted Style - Team AWS
- Amazon S3 Path Deprecation Plan
- How to Configure Path-Style Access in Amazon S3 with Java SDK: A Complete Guide — javathinking.com
- Efficient LocalStack: S3 Endpoint Configuration
- Won't duke, duke, duke the URLs: AWS backtracks on plans to block old-style S3 paths
- Update to Amazon S3 Path Deprecation Plan


