If example.com should use files stored in a physical /public directory, decide first whether the folder should remain hidden. An internal rewrite serves /public while the browser keeps showing https://example.com/path. An external redirect sends a 3xx response and changes the browser address to https://example.com/public/path.
Need a server to try this on? Antihost offers Linux VPS plans with NVMe storage, full root access and a 14-day free trial.
For a root .htaccess file, the working internal-rewrite pattern is:
As an Amazon Associate I earn from qualifying purchases.
Recommended Free Tools
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?:www.)?example.com$ [NC]
RewriteCond %{REQUEST_URI} !^/public(?:/|$) [NC]
RewriteRule ^ public%{REQUEST_URI} [END]
The host check belongs in RewriteCond, the rule pattern is relative because this is per-directory configuration, and the destination-prefix condition prevents repeated /public additions.
Choose between a hidden subfolder and a visible redirect
“Redirect the domain to a subfolder” can describe two different Apache operations. Choosing the wrong one produces either an unexpected URL change or a rewrite loop.
#1 Best Overall
| Goal | Apache operation | Browser address | Typical use |
|---|---|---|---|
| Serve the site from a folder without exposing it | Internal rewrite (path substitution without R) |
Remains https://example.com/path |
A document root contains a public directory and that directory should be an implementation detail. |
| Tell clients that the public URL includes the folder | External redirect (fully qualified URL or R flag) |
Changes to https://example.com/public/path |
A URL migration or deliberate public path change. |
An internal rewrite is normally the answer when the requirement is “point the root domain at /public without showing the folder.” A redirect is appropriate only when clients, bookmarks and search engines should receive the new URL.
Prerequisites and assumptions
The examples below assume all of the following:
- DNS for
example.comalready resolves to the Apache site handling the request. - The
.htaccessfile is in the document root, not inside/public. - The real files are in a physical document-root directory named
public. - Apache’s
mod_rewritemodule is enabled. - The virtual host permits the required
.htaccessoverrides. A server that disallows overrides will silently ignore or reject these rules.
Confirm the actual document root and the directory containing the active .htaccess. A rule in a file inside /public is evaluated in a different per-directory context and cannot be copied unchanged from the document-root example.
Configure an internal rewrite that keeps /public hidden
Use this root .htaccess pattern
RewriteEngine On
# Apply only to the intended host.
RewriteCond %{HTTP_HOST} ^(?:www.)?example.com$ [NC]
# Do not prepend /public a second time.
RewriteCond %{REQUEST_URI} !^/public(?:/|$) [NC]
# In document-root .htaccess, the RewriteRule pattern is relative.
RewriteRule ^ public%{REQUEST_URI} [END]
Replace example.com with the host you have deliberately chosen to serve. The optional www. in the condition allows both example.com and www.example.com; remove it if only one host should be accepted, or add a separate canonical-host redirect as described later.
What each directive does
| Directive or fragment | Purpose |
|---|---|
RewriteEngine On |
Enables mod_rewrite processing for this file. |
RewriteCond %{HTTP_HOST} ... [NC] |
Limits the rule to the allowlisted host name. [NC] makes the comparison case-insensitive. |
RewriteCond %{REQUEST_URI} !^/public(?:/|$) [NC] |
Skips requests already beginning with /public, whether the path is exactly /public or continues below it. The negation mark (!) is the loop guard. |
RewriteRule ^ |
Matches the path presented to this document-root .htaccess. In per-directory context Apache removes the directory prefix before matching, so the pattern starts with ^, not ^/. |
public%{REQUEST_URI} |
Maps the requested URI to the physical public directory while leaving the client unaware of the mapping. |
[END] |
Stops per-directory rewrite processing for the request on Apache versions that support this flag. |
For a request such as /docs/start.html, Apache internally looks for public/docs/start.html, but it does not send a redirect response. The address bar therefore remains /docs/start.html. A request for the root URI is mapped to the public directory itself.
If [END] is unavailable
Some older Apache versions or hosting environments do not provide [END]. Use [L] instead, and keep the destination-prefix exclusion in place:
Rank #2
RewriteRule ^ public%{REQUEST_URI} [L]
[L] ends the current rewrite pass, not every later per-directory pass. The exclusion condition is therefore essential when using it. Verify the Apache version and test the complete request flow before deploying.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteConfigure a visible redirect when the folder belongs in the URL
Start with a temporary redirect
Use a fully qualified destination and a temporary status while checking the behavior:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^(?:www.)?example.com$ [NC]
RewriteCond %{REQUEST_URI} !^/public(?:/|$) [NC]
RewriteRule ^ https://example.com/public%{REQUEST_URI} [R=302,END,NE]
The R=302 flag makes Apache send a temporary redirect. The browser should receive a Location header such as https://example.com/public/docs/start.html. The NE flag prevents Apache from unnecessarily escaping characters that are already part of the intended URL.
Change to permanent status only after testing
After you have checked both HTTP and HTTPS requests, existing and missing paths, query strings, host variants and canonical-host behavior, change R=302 to R=301 only if the change is genuinely permanent:
RewriteRule ^ https://example.com/public%{REQUEST_URI} [R=301,END,NE]
Browsers and intermediary caches can retain a 301 response. Clear cached permanent redirects or use a fresh test profile when validating a changed rule.
Rank #3
Understand matching context before changing the pattern
RewriteRule matches a path, not a hostname
Apache does not pass the domain name to the RewriteRule pattern. A pattern such as ^example.com is looking for those characters in the URL path, not testing the host. Host selection belongs in a condition using %{HTTP_HOST}, as in the examples above.
Per-directory rules do not start with a slash
In a document-root .htaccess, Apache strips the directory prefix and leading slash before applying the rule pattern. A request for /about is therefore matched as about. Writing ^/about in this context normally fails to match. Virtual-host configuration has different matching rules, so a snippet copied from the main Apache configuration may need its leading slash removed for .htaccess.
The file’s location changes the relative path
If the file is placed in /public/.htaccess, Apache is already operating relative to that directory. A rule intended for the document root can then prepend public again or point at the wrong location. Put the supplied root rule in the document-root file, or rewrite the pattern specifically for the directory where the file lives.
Paths, query strings and existing files
The substitution uses %{REQUEST_URI}, so the requested path is carried into the public tree. Query strings are normally retained when a rewrite substitution does not provide a new query string. For example, a request for /search?q=apache is internally handled as public/search with ?q=apache still available to the application.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Do not add a second question mark or manually concatenate a query string unless you intentionally want to replace or append parameters. If an application generates its own front-controller rules, place the directory mapping where it will not be undone by a later rule, and inspect the resulting request with the server’s rewrite trace or error log when available.
The mapping does not guarantee that a target exists. A missing public/assets/site.css remains a missing resource and should produce the site’s normal 404 behavior. Test a real static file and a deliberately nonexistent path rather than assuming every request is valid.
Test the result before making it permanent
Use headers-only requests
curl -I shows status and response headers without downloading the body. Substitute your real host and paths:
curl -I http://example.com/
curl -I https://example.com/
curl -I https://example.com/docs/start.html
curl -I https://example.com/assets/site.css
curl -I https://example.com/does-not-exist
curl -I https://www.example.com/
curl -I https://example.com/search?q=apache
curl -I https://example.com/public/docs/start.html
Expected results by configuration
| Request | Internal rewrite | Visible redirect |
|---|---|---|
| Root or nested public path | Final response is the application or file response; no redirect is required and the URL stays unchanged. | A 302 during testing, or 301 after approval, with Location: https://example.com/public.... |
| Existing static file | Served from public while retaining the requested URL. |
Redirects first, then the client requests the /public URL. |
| Missing path | Normal application or web-server 404, without repeated prefixing. | One redirect to the corresponding /public path, followed by the normal 404 if that target is absent. |
Request already beginning /public |
Excluded from the prefixing rule so it is not changed to /public/public.... |
Also excluded, preventing a redirect chain. |
| Query string | Should remain available unless another rule replaces it. | Should appear after the redirected path unless another rule deliberately changes it. |
Inspect the complete redirect chain with curl -I -L only after the single-response checks are clear. A chain that keeps adding /public indicates that the exclusion condition is missing or does not match the actual path.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Troubleshoot common failures
The rule never matches
- Check that the file is the active document-root
.htaccess, not a similarly named file in another directory. - Remove a leading slash from the
RewriteRulepattern in per-directory context. - Confirm that
mod_rewriteis enabled and that the virtual host allows the required overrides. - Verify that the request is reaching the expected virtual host. A different host definition can select another document root or another configuration.
Apache returns a 500 error
Read the Apache error log for the exact directive error. Typical causes include a misspelled flag, unsupported [END] on an older version, or rewrite directives being disallowed in .htaccess. Temporarily replace [END] with [L] only after confirming that the destination-prefix condition remains present.
The browser shows repeated /public segments
This is a rewrite or redirect loop. The rule is processing a URI that is already under the destination prefix. Ensure the condition is exactly broad enough to exclude both /public and descendants: !^/public(?:/|$). Also check that a second rule elsewhere is not adding the same prefix.
Best Value
The hostname condition does not behave as expected
Check the value received in HTTP_HOST, including a port in some development requests and the presence or absence of www. Keep the condition narrowly allowlisted rather than matching arbitrary host input. If you need one canonical host, use an explicit condition and fixed destination, not a destination assembled from whatever host the client supplied.
HTTPS rules conflict with a proxy
When TLS terminates at a reverse proxy, Apache may see the connection to the origin as HTTP even though the visitor used HTTPS. Before adding an HTTP-to-HTTPS rule, establish which request variable your proxy sets to represent the original scheme and whether it is trusted. Stacking an unconditional HTTPS redirect on top of an unverified proxy header can create a redirect loop.
The file is served from the wrong place
Confirm the physical path and permissions for public, then request a known file directly through the expected URL. A successful rewrite cannot serve a directory that is outside the selected document root or blocked by filesystem permissions. If only some extensions fail, inspect later application or content-negotiation rules rather than changing the host condition.
Protect canonical hosts and redirect destinations
A redirect URL must not be built from unvalidated user input. If an attacker can influence the host value that Apache copies into a Location header, they may turn the rule into an open redirect. The fixed https://example.com destination in the visible-redirect example avoids that problem. Keep the HTTP_HOST condition allowlisted, and add explicit rules for any other legitimate host instead of accepting arbitrary names.
Decide separately whether www.example.com or example.com is canonical. The subfolder mapping and host canonicalization solve different problems. Test every combination of HTTP/HTTPS and both host variants so that only the intended number of redirects occurs. If a proxy, load balancer or CDN performs part of this process, document which layer owns scheme and host normalization.
Use virtual-host configuration when you control Apache
If you can edit the main Apache configuration, a dedicated virtual-host Redirect is Apache’s preferred mechanism for a simple host move. It avoids per-directory rewrite processing and makes the host boundary explicit. The examples in this article target the common shared-hosting situation in which only .htaccess is available; in that situation, mod_rewrite is the appropriate mechanism.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Do not combine a virtual-host redirect and an equivalent .htaccess redirect without a reason. Two layers can produce duplicate hops, hide which rule is active and make troubleshooting harder.
Quick Recap
Deployment checklist
- Confirm DNS points the intended domain to the Apache virtual host and identify the document root.
- Choose internal rewriting if
/publicshould stay hidden; choose an external redirect if it should become part of the public URL. - Back up the current
.htaccessfile. - Place the rule in the document-root file and use a relative
RewriteRulepattern. - Allowlist the host in
RewriteCond %{HTTP_HOST}and use a fixed redirect destination. - Keep the destination-prefix exclusion so an already mapped request is not processed again.
- Use
[END]where supported; otherwise use[L]with the exclusion intact. - Test root, nested paths, an existing static file, a missing path, query strings, both host variants and both schemes with
curl -I. - For visible redirects, keep
R=302until theLocationheader and final content are correct. - Change to
R=301only for a permanent move, and clear cached permanent redirects when retesting.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

