RoastMyOpsec

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.

OptionWhen it winsWatch-outTakeaway
.env on the originNeverApp secretsIncident; roast signatures this path
terraform.tfstate on the originNeverState can hold the same class of secretsSame class of miss; deny the name
Remote state backendTeam Terraform you controlA public ACL on the backend recreates the missNot the website CDN
robots.txt DisallowNot for state filesAdvertises the pathEdge 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 now

FAQ

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