RoastMyOpsec

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.

OptionWhen it winsWatch-outTakeaway
/.htaccessApache origin config, not a CDN objectDownloadable text is a leakDeny at the edge; not a scored roast path
/.user.iniPHP per-dir settings on the app hostSame if GET returns the fileSame class of miss as /.htaccess
/.envNever on the CDNLive secretsRoast signatures this path today
robots.txt DisallowNot for server configAdvertises the filenamesCDN 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 now

FAQ

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