Vulnerabilities
.user.ini vs .htaccess on Production Origins
February 15, 2025
.user.ini vs .htaccess on a production origin is two server config files that must not be downloadable next to index.html. .htaccess is Apache per-directory rules. .user.ini is PHP's per-directory INI. Both can name paths, limits, and sometimes credentials or auto_prepend hints. Serving either is the same class of miss as /.env. RoastMyOpsec does not currently score /.user.ini or /.htaccess. It does probe other leftovers. Still deny those names at the edge. Do not Disallow them in robots.txt.
The practical difference
Apache reads .htaccess when overrides are on. PHP reads .user.ini in the document root and below. Nginx ignores .htaccess but will still serve the file as a public object if the origin has no deny rule. The leak is the text.
Cousins include .htaccess.bak and php.ini souvenirs in zip backups. See dump.sql vs backup.zip and .htaccess vs web.config. The fix is the same: keep server config off the public bucket, deny the names at the CDN.
How to choose based on what you shipped
CI should fail if .user.ini or .htaccess appear in a static artifact that is not supposed to configure a server. CDN should deny those basenames even on PHP origins so a mis-set MIME type cannot serve them as text. If you need the file for the real origin, it still must not be world-downloadable as a static object.
| Option | When it wins | Watch-out | Takeaway |
|---|---|---|---|
| /.htaccess | Apache origin config, not a CDN object | Downloadable text is a leak | Deny at the edge; not a scored roast path |
| /.user.ini | PHP per-dir settings on the app host | Same if GET returns the file | Same class of miss as /.htaccess |
| /.env | Never on the CDN | Live secrets | Roast signatures this path today |
| robots.txt Disallow | Not for server config | Advertises the filenames | CDN deny is the control |
When each config file wins (never as a public download)
.htaccess wins as origin configuration Apache actually applies — and even then, prefer server config that is not a downloadable object. .user.ini wins only as PHP runtime config on the app host with the same download deny. Neither wins in an S3 brochure bucket.
A 404 on /.htaccess is not proof /.user.ini is gone. Deny the family. Pair with directory listing if autoindex would have shown the names.
Common mistakes
The first mistake is uploading the PHP tree to object storage including .user.ini and .htaccess.
The second mistake is gitignoring .env and still committing .user.ini with a path or credential-shaped value.
The third mistake is Disallowing those names in robots.txt and calling it access control.
What a URL roast can prove
The roast does not currently score /.user.ini or /.htaccess. It signatures /.env, /.git/HEAD, /package.json, and some backup/.DS_Store class probes. Still deny the PHP and Apache config names. Bounded GETs only. No php.ini parse as an exploit recipe. Confirm the bucket, not only a soft-404.
Free audit the URL you own
RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.
Free audit nowFAQ
- Is a downloadable .user.ini a vulnerability?
- It is an information leak. Remove it from public objects. If it contained secrets, rotate them. Deny the name at the CDN.
- Does RoastMyOpsec fetch .user.ini or .htaccess?
- Not as scored paths today. It does probe other well-known leftovers. Deny these names anyway. Same class of miss as /.env.
- Do I need .user.ini on a CDN-only site?
- Usually no. PHP INI belongs on the app origin that runs PHP, and even there it must not be a world-readable static file.
Sources
Related guides
Vulnerabilities
.htaccess vs web.config on a Public OriginVulnerabilities
robots.txt Disallow vs Access Control for Hidden PathsVulnerabilities
Directory Listing vs 403 for Backup and Static Folders