Your production website has HTTPS, a WAF, MFA, monitoring, and regular updates.
Then there's staging.example.com.
Nobody has touched it for six months.
It's running an older version of the application, has debug mode enabled, contains a copy of the production database, and still uses the test admin account everyone on the team knows.
You probably don't think of it as a security problem.
An attacker does.
If a staging website is publicly accessible, it is part of your attack surface. It doesn't matter whether the URL is linked from your homepage or whether you call it “temporary.”
The internet doesn't care what you named the server.
Why staging sites are so easy to attack
Production environments usually get attention.
Staging environments get forgotten.
That's understandable. Developers need somewhere to test new features. They need realistic data. They need debugging tools. They need access to logs and internal services.
So staging often gets configured for convenience rather than security.
You might find:
staging.example.com
dev.example.com
test.example.com
qa.example.com
demo.example.com
old.example.com
And one of them is probably doing something it shouldn't.
Maybe it's running an old WordPress plugin.
Maybe the admin password is test123.
Maybe .git is exposed.
Maybe the application is running with:
DEBUG=true
Maybe the staging database contains real customer records.
None of those problems are visible on the production homepage.
That's why checking only your main domain gives you a false sense of security.
“It's not linked anywhere” doesn't protect it
This is one of the most common assumptions.
Someone says:
> “Nobody knows the staging URL.”
That isn't a security control.
Subdomains can be discovered through DNS records, certificates, search indexes, leaked documentation, code repositories, browser history, or simple enumeration.
Certificate Transparency logs are particularly useful here. Publicly trusted TLS certificates are logged in Certificate Transparency systems, which means subdomains included in certificates can sometimes be discovered even if you never linked them publicly. The CA/Browser Forum's requirements make CT logging a normal part of publicly trusted certificate issuance. (certificate-transparency.org)
So an attacker doesn't necessarily need to guess:
staging.example.com
They may already have clues that the hostname exists.
And once they find it, the interesting question becomes:
What's running there?
The old code problem is bigger than you think
Let's say your production application is fully patched.
Your staging environment isn't.
That's a problem.
Suppose your application uses a vulnerable package.
Production was updated last week.
Staging still uses the old version because nobody wanted to risk breaking the test environment.
An attacker discovers staging.
Now they have a vulnerable application that may expose useful information about your stack.
Even if they can't jump directly from staging into production, they may learn:
- Framework versions
- API endpoints
- Internal hostnames
- Database structure
- Authentication mechanisms
- File paths
- Third-party services
- Development credentials
- Feature flags
- Unreleased functionality
Staging can become reconnaissance material.
And sometimes the environment is connected to production services anyway.
That's where the risk becomes much more serious.
Never put real customer data in a public staging environment
This is the rule I'd be most strict about.
If your staging site is publicly accessible, don't casually copy your production database into it.
A developer might say:
> “We need realistic data to test this feature.”
I understand why.
But there are safer ways to get realistic data.
Use synthetic data.
Anonymize production data.
Remove unnecessary fields.
Separate credentials.
Restrict access.
Don't create a second public copy of your customers' information just because it's convenient for testing.
Imagine your production database contains:
Name
Email
Phone
Address
Orders
Invoices
Password hashes
Support tickets
Now imagine the same data exists in a forgotten staging environment running an old version of your application.
You've doubled the places an attacker can look.
The production database may have excellent controls.
The staging copy may not.
Debug mode should not be casually exposed
Development environments often need detailed errors.
Production doesn't.
A framework may show:
SQL query
File path
Stack trace
Environment variables
Framework version
That information is fantastic when you're debugging your own application.
It's also useful to someone trying to attack it.
For example, a verbose exception might reveal:
C:\projects\myapp\src\controllers\UserController.js
or:
/var/www/staging/api/
That doesn't automatically mean the application is compromised.
But it gives an attacker a much better map.
If your staging environment needs detailed errors, restrict access to the environment instead of exposing those errors to the entire internet.
Your test credentials can become production credentials
Here's another dangerous shortcut.
A developer creates:
admin@example.com
Password123!
for staging.
Then someone uses the same password in production.
Or the staging server has an API token that also works against production.
Now an attacker doesn't need to “break into production.”
They can attack the weaker environment and look for credentials or tokens that cross the boundary.
This is why credential separation matters so much.
Staging should have its own:
- Database credentials
- API keys
- OAuth credentials
- Cloud credentials
- Payment keys
- Admin accounts
- Deployment tokens
And production credentials shouldn't be available to staging unless there's a very specific reason and tight controls around them.
A staging environment should be allowed to fail without taking production down with it.
Basic authentication is better than a secret URL
If staging really needs to be public, at least put an authentication layer in front of it.
For example, your reverse proxy can require HTTP Basic Authentication:
location / {
auth_basic "Staging";
auth_basic_user_file /etc/nginx/.htpasswd;
}
That isn't the strongest possible access-control design, but it's much better than:
> “Nobody knows the URL.”
For internal staging, I'd go further.
Use a VPN, identity-aware proxy, IP allowlist, or another access-control layer so the environment isn't reachable by everyone on the internet.
The exact choice depends on your infrastructure.
The principle doesn't change:
If the staging site doesn't need to be public, don't make it public.
Don't forget the forgotten staging server
This is where website owners should pay attention.
You might have hired a developer three years ago.
They created:
old.example.com
to test a redesign.
The redesign was abandoned.
The DNS record stayed.
The server stayed.
The SSL certificate stayed.
Nobody remembers the login.
That's an asset nobody is monitoring.
And that's exactly why old infrastructure is dangerous.
I would periodically inventory:
Domains
Subdomains
Cloud instances
CDN endpoints
Load balancers
Object storage
Databases
Third-party SaaS
Then ask:
Does this still need to exist?
If the answer is no, remove it.
Deleting unused infrastructure is often better security than adding another security product.
Scan staging exactly like production
Here's the mindset I'd recommend to developers:
If staging is reachable from the internet, scan it.
Don't exclude it from security testing just because it isn't production.
Check for:
- Known vulnerabilities
- Outdated dependencies
- Exposed services
- Weak authentication
- Sensitive files
- Security headers
- TLS configuration
- Unexpected endpoints
- Public storage
- Debug interfaces
- Vulnerable plugins
- Misconfigured APIs
For example:
curl -I https://staging.example.com
Check the headers.
Then:
nmap -sV staging.example.com
Check what services are exposed.
For your own infrastructure, vulnerability scanning can then go deeper into the actual application.
The point isn't to turn staging into an impenetrable fortress.
It's to make sure the test environment isn't significantly weaker than the system you're testing.
A staging security setup I'd actually use
If I were setting up a new application today, I'd separate things like this:
Internet
|
┌──────▼──────┐
│ Production │
│ WAF │
└──────┬──────┘
|
Production DB
Developer / Team
|
┌────▼─────┐
│ Staging │
│ Access │
└────┬─────┘
|
Staging DB
Synthetic Data
The staging environment gets its own database.
Its own credentials.
Its own API keys.
Its own deployment path.
And ideally, access is restricted to the people who actually need it.
What I would not do is:
Staging
↓
Production Database
↓
Production API Keys
↓
Production Cloud Account
That's not staging.
That's production with a weaker front door.
What website owners should ask their developer
You don't need to understand DevOps to catch a risky staging setup.
Ask these questions:
Do we have any public staging or test websites?
Can anyone on the internet access them?
Do they contain real customer data?
Are staging credentials different from production?
Can staging access production databases or APIs?
Are staging environments included in vulnerability scans?
Who owns old subdomains and test servers?
Those seven questions can reveal more than a fancy security report.
If your developer says:
> “We don't have staging.”
That's fine too.
Not every small website needs a separate staging environment.
The security problem is not having staging.
The problem is having an exposed environment you forgot about.
Should you delete your staging site?
Sometimes.
If the project is finished and nobody uses the environment, shut it down.
If you need it, secure it.
If it's temporary, give it an expiration date.
This last one is surprisingly effective.
Instead of creating:
staging.example.com
and leaving it there forever, create temporary environments that are automatically destroyed after the feature is merged or the project is finished.
Modern infrastructure makes this possible.
Temporary infrastructure is often safer than permanent infrastructure because there's less forgotten state.
The best forgotten server is the one that no longer exists.
The bigger problem is visibility
You can't secure infrastructure you don't know exists.
That's true for production.
It's even more true for staging.
Your security process should have some way to discover changes:
New subdomain
New server
New exposed port
New certificate
New dependency
New vulnerability
New deployment
Then you can investigate before the forgotten environment becomes an entry point.
This is also why website security shouldn't be treated as a once-a-year penetration test.
A test tells you what existed when the test happened.
Your infrastructure changes tomorrow.
A new staging server appears.
A developer deploys a test API.
A certificate gets issued.
A vulnerable package gets installed.
Security has to follow those changes.
That's where continuous monitoring and scanning are useful. Torlyx combines vulnerability scanning with SSL and uptime monitoring, real-browser checks, WAF protection, and pre-deployment code scanning, so staging and production can be treated as part of the same security lifecycle.
If it's online, treat it like production
You don't need to spend the same amount of money protecting staging as you do production.
But you should apply the same basic security thinking.
If it's publicly reachable, assume someone will find it.
If it contains sensitive data, protect that data.
If it runs software, patch it.
If it has credentials, separate them.
If nobody needs it, shut it down.
And if it exists only because someone might need it someday, put an expiration date on it.
Today, search your DNS records for staging, dev, test, qa, demo, and old.
Open every hostname you find.
For each one, ask one question:
“If an attacker found this right now, what would they get?”
Start with the one that gives you the worst answer.