.htaccess on BrewInTH: which PHP rules work and which to fix

Moving a PHP site with .htaccess to BrewInTH? The platform reads it at release. See which rules it covers, converts or ignores, and how to fix the rest.
You moved a PHP site from your old host. The home page opens, but a link like /about lands on the home page, an old link that used to forward to a new page goes nowhere, or a folder that asked for a password now opens for anyone. Most of the time the cause is a small file called .htaccess, which cPanel-style hosts have relied on for years and your site quietly depended on.
BrewInTH does not run Apache, so nothing reads .htaccess on every visit the way your old host did. Since mid-October 2026, though, the platform reads the file for you at each release, converts the rules it can, and tells you which ones it cannot. This guide shows what happens to each kind of rule on BrewInTH, where to see the result, and how to fix what is left.
In short
🧙 PHP sites on BrewInTH run on Caddy (FrankenPHP), not Apache; the platform reads.htaccessat every release
✅ Sending every address toindex.php(WordPress, Laravel) and forcing https are done by the platform already
🔁 SimpleRewriteRulelines and 301/302 redirects become routes of the site
⚠️Redirect 301,ErrorDocument,Header,php_valueand folders locked with Deny or a password have no effect
📋 The result shows under What the platform did and in the Preflight box
What .htaccess is, and what is different on BrewInTH
.htaccess is a settings file for the Apache web server. Most cPanel hosts run Apache, or a server that reads the same file. On every visit the server reads it and follows the rules inside: turning a friendly address into a real PHP file, forwarding an old link to a new page, or keeping people out of a folder.
PHP sites on BrewInTH run on Caddy (FrankenPHP), which does not read .htaccess. So the platform reads it instead, when you release. RewriteRule lines it can convert become routes of the site, and the ones it cannot are reported. The .htaccess file stays with the site, but anyone who asks for it in a browser gets a 404.
This happens however a PHP site arrives as files: the Upload & go live page (a ZIP or a cPanel backup), an AI agent connected with a link and token, the Build & publish button on the Releases tab, and Publish from SFTP. The file that counts is the .htaccess at the top level of the site, next to index.php. An .htaccess inside a sub-folder is not read.
The three groups of rules

Group 1: the platform does it already
- Sending every address without a real file to
index.php. This is the core rule of WordPress, Laravel, CodeIgniter and any site that does its own routing. On BrewInTH, an address that matches no file goes toindex.phpanyway, so WordPress links like/2026/10/my-post/work straight away. - Forcing https. Every address of the site is sent from http to https, and pages carry HSTS already (what HTTPS is and how BrewInTH sets it up).
- WordPress's two rules that change nothing:
^index\.php$ - [L]and the line that passes the Authorization header on.
Group 2: converted into routes of the site
Simple RewriteRule lines are converted at release, for example:
RewriteRule ^news/([0-9]+)/?$ news.php?id=$1 [L,QSA]
RewriteRule ^product/([a-z0-9-]+)$ index.php?page=product&slug=$1 [L]
RewriteRule ^old-page\.html$ /new-page [R=301,L]
/news/12/runsnews.php?id=12, and/product/red-shirtrunsindex.php?page=product&slug=red-shirt.- Old links are forwarded with a 301 or a 302, to another site too if the target is a full
https://…address. - The flags that work are
L,QSA,NC,R(includingR=301andR=302),NE,ENDandPT. The only conditions that work are "not a file" and "not a folder" (RewriteCond %{REQUEST_FILENAME} !-fand!-d). RewriteBaseis read as well, so a site that lived in a sub-folder of the old host converts correctly.
Group 3: no effect, fix it another way
- RewriteRule lines with any other condition, such as the referring site (
HTTP_REFERER, used for hotlink protection), the browser (HTTP_USER_AGENT, used to block bots),QUERY_STRING, or%{REQUEST_FILENAME}\.php -f. - Rules with other flags, such as
[F](forbidden), and some regex features, such as(?!…). - Apache directives that are not RewriteRule:
Redirect 301,RedirectMatch,ErrorDocument,Header,ExpiresByType,SetEnv,Deny from all,Require all denied, andAuthTypewith an.htpasswdfile. php_valueandphp_flag.
Where to see the result

The platform reports the result in three places, depending on how you hand the files over:
- The Upload & go live page. After the upload, look under What the platform did. Lines starting with
.htaccess:say how many rules were converted, with examples such as/news/1/ → /news.php?id=1, and which rules could not be, with the addresses that will not work. If the file holds only rules the platform does anyway, it says nothing needs doing (how the Upload & go live page works). - The project's Releases tab. The Preflight box has a line about the
.htaccessin the selected ZIP. - An AI agent sees the same list in its preflight check, and the BrewInTH guide tells it to move rules that cannot be converted into
index.php.
The summary does not mention every line. When the file also has RewriteRule lines, directives such as Redirect 301 or Deny from all go unmentioned. So once the site is live, open your important addresses yourself on the BrewInTH link before you switch the domain.
Fixing the rules that do not work
Old links that use Redirect 301
Redirect and RedirectMatch are a different directive from RewriteRule, and the platform does not convert them. Rewrite them as RewriteRule lines and send the files again:
# Before: not read by the platform
Redirect 301 /old-page.html /new-page
# After: converted by the platform
RewriteRule ^old-page\.html$ /new-page [R=301,L]
Links without .php (/about instead of /about.php)
Many PHP sites use this rule so that /about opens about.php:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [L]
The %{REQUEST_FILENAME}\.php -f condition cannot be converted, so the whole rule has no effect and /about goes to index.php instead. For a site with a handful of pages, write one rule per page; those convert fine:
RewriteRule ^about/?$ about.php [L]
RewriteRule ^contact/?$ contact.php [L]
For a site with many pages, let index.php do the routing, with the prompt below.
Your own 404 page (ErrorDocument)
On a PHP site with index.php at the top level, any address without a real file goes to index.php. If index.php is just the home page, a mistyped address shows the home page instead of a 404. The fix is to have index.php check the address first and answer a 404 page with a 404 status when the page does not exist.
Everything else: let AI move it into index.php
Copy the .htaccess: lines from the summary and give them to your AI chat or coding agent, together with index.php and .htaccess:
This PHP site moved from Apache to BrewInTH, which runs Caddy (FrankenPHP) and does not read .htaccess.
Every address without a real file is sent to index.php.
These rules could not be converted: (paste the .htaccess: lines from the summary here)
1. Move what these rules do to the top of index.php, reading the address from $_SERVER['REQUEST_URI']
2. Answer an address that matches no page with a 404 page and http_response_code(404)
3. Do not change unrelated files
4. Send back the complete index.php, with a list of which address runs which file
When it is done, send the new files to the same project and read the summary again.
Watch out: what .htaccess used to protect

The files most PHP sites need to hide are hidden already, without any rule: files and folders whose names start with a dot, such as .env and .git (except /.well-known/); .sql, .sqlite, .db and .log files; error_log and cgi-bin; and composer.json and README.md at the top level. Folder listings are off too.
Locks written in .htaccess, however, do nothing here. Check these three things before the site goes live:
- Folders closed with
Deny from allorRequire all denied, such as a backup or includes folder: the files inside can be opened from the web. Leave files the site does not need, such as backups, out of what you upload; for files the site does need, have AI move the protection into the code. - Folders behind an
.htpasswdpassword, such as an admin area, open without asking for one. Have AI add a login in PHP before you go live. - Non-
.phpfiles the rules used to guard, such asconfig.inc,settings.ini, or.bakand.zipfiles in the site's folder, can be read or downloaded in full. Move passwords to the App settings tab and read them in code withgetenv()(keep API keys and passwords safe).
Other things people set in .htaccess
www or no www. Rules that force or remove www. are skipped, and the summary counts them with the rules the platform handles. At the time of writing, BrewInTH does not send one name to the other: if you connect both example.com and www.example.com in the Domains tab, both open the same site. Pick the name the site uses as its link with Use as the main link. If you want the address bar to show one name only, have AI add a 301 redirect at the top of index.php (use your own domain with BrewInTH).
PHP settings come from the package, so php_value and php_flag lines have no effect. The upload limits (upload_max_filesize and post_max_size) are 20 MB on Free and 400 MB on paid packages. PHP error messages are not shown on the page; they go to the Logs tab. Settings PHP lets code change itself, such as memory_limit, can be set with ini_set().
SetEnv and Header. Values you set with SetEnv go in the App settings tab: fill in Name and Value, then press Save, and read them in code with getenv(). Values saved after the latest release take effect at the next release. Headers for PHP pages are sent from code with header(), and compression is done for you at the platform's entrance.
FAQ
Should I delete .htaccess before uploading?
No. Send it with the site: the platform reads its rules from it, and nobody can open the file from the web.
I changed .htaccess. What now?
Release again, because the rules are read at release. Send the new ZIP to the same project on Upload & go live, press Publish from SFTP if you edited with FileZilla (upload files with SFTP), or have your AI agent release again.
Does a WordPress site need anything for .htaccess?
Usually not: the platform covers WordPress's standard rules. If a caching plugin wrote long rules into the file, the summary warns that they cannot be converted. The site still works through PHP, but whatever the plugin did through .htaccess, including a security plugin's protections, does not run.
Can a plain HTML (static) site use .htaccess rules?
No. A static site has no RewriteRule; an address without a file gets a 404, or the site's own 404.html. For links without an extension, keep each page as a folder, such as about/index.html, and link to /about/.
Are the rules converted when the site is released from Git (GitHub or GitLab)?
Not at the time of writing: a release from Git does not read .htaccess. If the site needs those rules, move the routing into index.php.
Summary
Moving a PHP site to BrewInTH does not mean throwing .htaccess away. The basics, sending every address to index.php and forcing https, are done by the platform. Simple RewriteRule lines and redirects are converted at release. For the rest, the summary tells you which addresses will not work: move those rules into index.php, rewrite Redirect 301 as RewriteRule, check the folders you used to lock, and test your important addresses on the BrewInTH link before you switch the domain.
Open the Upload & go live page →
