Vulnerabilities
Terraform tfstate vs .env on Production Origins
January 30, 2026
Terraform tfstate vs .env on a production origin is two files that must never be public objects. /.env is app secrets — the roast signatures it. terraform.tfstate is a JSON snapshot of infrastructure that HashiCorp documents as sensitive: resource attributes can include passwords, tokens, and private endpoints. Serving it next to index.html is the same class of miss as /.env. RoastMyOpsec may not fetch tfstate today. Still deny that name at the edge. Do not list it in robots.txt.
The practical difference
.env is what the website process reads. tfstate is what Terraform reads to remember the cloud. Different tools, same rule: the marketing bucket is not a state backend.
Remote state with encryption and lock is the intended home. A leftover terraform.tfstate in a static export is an accident that looks like a brochure file until someone downloads it.
How to choose based on what you shipped
If tfstate was public: take it off the origin, deny the basename at the CDN, rotate secrets that may have been in state, and move state to a private backend. Fail CI if .tfstate appears in a frontend artifact. Pair with /.env if the same zip held both.
| Option | When it wins | Watch-out | Takeaway |
|---|---|---|---|
| .env on the origin | Never | App secrets | Incident; roast signatures this path |
| terraform.tfstate on the origin | Never | State can hold the same class of secrets | Same class of miss; deny the name |
| Remote state backend | Team Terraform you control | A public ACL on the backend recreates the miss | Not the website CDN |
| robots.txt Disallow | Not for state files | Advertises the path | Edge deny instead |
When each file wins (never as a static download)
.env wins as private app runtime config. tfstate wins in a locked, private backend. Local tfstate wins only on a workstation that never syncs that folder to a public bucket.
Marking variables sensitive redacts CLI output. It does not make a public tfstate safe. HashiCorp's own guidance treats state as sensitive data.
Common mistakes
The first mistake is keeping terraform.tfstate in the same repo as the marketing site and deploying the repo root.
The second mistake is rotating .env values and leaving state on the CDN because 'that is infrastructure, not the app.'
The third mistake is Disallowing *.tfstate in robots.txt instead of blocking the objects at the CDN.
What a URL roast can prove
The roast signatures /.env. terraform.tfstate is not a scored path today. Still deny it. Bounded GETs only. No state parse as an exploit recipe, no cloud API use. Confirm the bucket. A homepage body on that path is a soft-404, not a pass for the file.
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 public terraform.tfstate a vulnerability?
- Treat it as a sensitive-data exposure. Remove it, deny the name at the edge, and rotate secrets that may have been in state.
- Does RoastMyOpsec fetch terraform.tfstate?
- Not as a scored path today. It does probe /.env. Deny tfstate at the CDN anyway. Same class of miss as /.env.
- Does sensitive = true make public state safe?
- No. That flag redacts typical CLI output. State can still hold the values. Keep state off public origins.
Sources
Related guides
Vulnerabilities
Exposed .git vs .env on Production WebsitesVulnerabilities
.env.example vs .env on Production OriginsVulnerabilities
Exposed dump.sql vs backup.zip on Production