Security dashboard showing HTTPS padlock, vulnerability scanner results and firewall activity on developer monitors
# developer tools# website monitoring

The Ultimate Guide to Website Security for Developers

Security vulnerabilities are discovered in web applications constantly. Some are sophisticated attacks requiring deep technical knowledge to execute. Many are basic mistakes — missing input validation, outdated dependencies, misconfigured headers — that are straightforward to prevent.

This guide covers the most important security practices for web developers: the vulnerabilities you need to understand, the defences you need to implement, and how monitoring fits into your security posture.


The Foundation: HTTPS Everywhere

Every web application should use HTTPS. This is no longer optional — browsers penalise non-HTTPS sites, Google uses HTTPS as a ranking signal, and users have come to expect the padlock.

What HTTPS provides:

  • Encryption — Data in transit is encrypted and can't be read by interceptors
  • Authentication — Users can verify they're talking to the real server
  • Integrity — Data can't be modified in transit without detection

Getting HTTPS right:

  • Use TLS 1.2 and 1.3 only — disable TLS 1.0 and 1.1
  • Configure strong cipher suites (ECDHE key exchange, AES-GCM encryption)
  • Enable HSTS (HTTP Strict Transport Security) to prevent downgrade attacks
  • Redirect all HTTP to HTTPS
  • Monitor certificate expiry

See complete guide to SSL certificates for implementation details and domain monitor for SSL certificate expiry monitoring.


HTTP Security Headers

Security headers are HTTP response headers that instruct browsers on security policies. They're one of the highest-leverage security improvements you can make — they take minutes to implement and address significant attack categories.

Strict-Transport-Security (HSTS)

Tells browsers to always use HTTPS for your domain, preventing SSL stripping attacks:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • max-age — How long browsers should enforce HTTPS (seconds). Start with a short value, increase once confirmed working.
  • includeSubDomains — Applies to all subdomains
  • preload — Submit to the HSTS preload list so browsers use HTTPS even before the first visit

Content-Security-Policy (CSP)

Defines which sources are allowed to load content on your page. Prevents cross-site scripting (XSS) and data injection attacks:

Content-Security-Policy: default-src 'self'; script-src 'self' cdn.example.com; img-src 'self' data: https:

CSP is the most powerful but most complex security header. Start in report-only mode to identify issues before enforcing:

Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

X-Frame-Options

Prevents your page from being embedded in iframes on other sites, blocking clickjacking attacks:

X-Frame-Options: DENY

Or if you need to allow framing from specific origins:

X-Frame-Options: SAMEORIGIN

X-Content-Type-Options

Prevents browsers from MIME-type sniffing — interpreting a file differently from its declared content type:

X-Content-Type-Options: nosniff

Referrer-Policy

Controls how much referrer information is sent when navigating away from your site:

Referrer-Policy: strict-origin-when-cross-origin

Permissions-Policy

Controls which browser features and APIs the page can use:

Permissions-Policy: camera=(), microphone=(), geolocation=()

Quick Check

Use securityheaders.com to scan any URL and see which security headers are missing or misconfigured. It grades your headers and provides specific recommendations.


Common Vulnerabilities and How to Prevent Them

SQL Injection

SQL injection occurs when user input is incorporated directly into database queries without sanitisation, allowing attackers to modify the query logic.

Vulnerable:

# DO NOT do this
query = f"SELECT * FROM users WHERE email = '{user_email}'"

An attacker can input '; DROP TABLE users; -- and execute arbitrary SQL.

Safe — use parameterised queries:

# Always use parameterised queries
cursor.execute("SELECT * FROM users WHERE email = %s", (user_email,))

ORMs (SQLAlchemy, Eloquent, ActiveRecord) use parameterised queries by default. Never construct SQL by string concatenation with user input.

Cross-Site Scripting (XSS)

XSS occurs when an attacker injects malicious JavaScript that executes in other users' browsers. It can steal session cookies, capture keystrokes, or redirect users.

Vulnerable:

<!-- Directly rendering user input -->
<div>{{ user.comment }}</div>

If user.comment contains <script>document.cookie</script>, that script executes.

Prevention:

  • HTML-encode output in templates — most modern template engines do this by default (Django, Jinja2, Blade, Twig)
  • Set a Content-Security-Policy that blocks inline scripts
  • Never use innerHTML or dangerouslySetInnerHTML with user-controlled content
  • Use httpOnly cookies so JavaScript can't access session tokens

Cross-Site Request Forgery (CSRF)

CSRF tricks authenticated users into making unwanted requests to your application. A malicious site can cause a logged-in user's browser to submit a form to your application.

Prevention:

  • Use CSRF tokens — a secret value included in forms and verified server-side
  • Most frameworks include CSRF protection by default (Django, Laravel, Rails)
  • Use SameSite=Strict or SameSite=Lax on session cookies
  • Check the Origin and Referer headers for state-changing requests

Insecure Direct Object References (IDOR)

IDOR occurs when an application exposes internal object references (like database IDs) and doesn't verify that the requesting user has permission to access that object.

Vulnerable:

GET /api/invoices/12345

If the application returns the invoice for ID 12345 without checking that the logged-in user owns it, any authenticated user can access any invoice by guessing IDs.

Prevention:

  • Always verify object ownership on every request — never assume that a user can access an object just because they're authenticated
  • Use non-sequential, non-guessable IDs (UUIDs) as an additional layer

Sensitive Data Exposure

Storing or transmitting sensitive data insecurely:

Common mistakes:

  • Storing passwords in plaintext — always hash with bcrypt, Argon2, or similar
  • Logging sensitive data (passwords, credit cards, PII) in application logs
  • Exposing sensitive data in API responses that should be filtered
  • Storing secrets in source code or unencrypted config files

Prevention:

  • Never log passwords or sensitive user data
  • Use .env files for secrets and ensure they're in .gitignore
  • Use secret management services for production (AWS Secrets Manager, Vault)
  • Hash passwords with strong algorithms — never MD5 or SHA-1

Security Misconfiguration

A broad category covering:

  • Default credentials not changed
  • Directory listing enabled
  • Verbose error messages showing stack traces to users
  • Unnecessary services running
  • Permissive file permissions

Prevention:

  • Disable directory listing in web server config
  • Show generic error messages to users; log details server-side
  • Run applications with least-privilege accounts
  • Review and close unnecessary open ports

Authentication Security

Authentication is a high-value attack target. Get it wrong and attackers bypass all your other security controls.

Password Storage

Always hash passwords with a modern adaptive algorithm:

from passlib.hash import argon2

# Hashing
hashed = argon2.hash(password)

# Verification
is_valid = argon2.verify(password, hashed)

Argon2 and bcrypt are the recommended choices. Never use MD5, SHA-1, or SHA-256 directly for passwords — these are fast hashing algorithms, making brute-force attacks easy.

Multi-Factor Authentication

Offer MFA. For sensitive applications, require it. Time-based one-time passwords (TOTP) via apps like Google Authenticator are the most common implementation.

Session Management

  • Generate session tokens with a cryptographically secure random generator
  • Use httpOnly cookies to prevent JavaScript from accessing session tokens
  • Use Secure flag to ensure cookies are only sent over HTTPS
  • Implement session expiry and rotation on privilege escalation
  • Invalidate sessions on logout and password change

Rate Limiting Login Attempts

Without rate limiting, brute-force attacks against login forms are trivial.

from flask_limiter import Limiter

limiter = Limiter(app)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")
def login():
    ...

Dependency Security

Your application's security includes all its dependencies. A vulnerability in a library you use is a vulnerability in your application.

Keep dependencies updated — Regularly update packages to get security patches. Outdated dependencies with known vulnerabilities are one of the most common attack vectors.

Use vulnerability scanning:

# Node.js
npm audit

# Python
pip-audit

# Ruby
bundle audit

Monitor for new vulnerabilities — GitHub's Dependabot automatically creates PRs for vulnerable dependencies. Enable it on your repositories.

Audit what you import — Understand what third-party packages you're using. Avoid packages with low maintenance, few users, or a history of security issues.


Infrastructure Security

Web Application Firewall (WAF)

A WAF sits in front of your application and filters malicious requests before they reach your application server. It blocks common attack patterns: SQL injection attempts, XSS payloads, known scanner signatures.

Cloudflare, AWS WAF, and similar services offer WAF capabilities. For most sites, Cloudflare's free tier provides meaningful protection.

DDoS Protection

A DDoS attack floods your server with traffic, making it unavailable. CDN and WAF providers (Cloudflare) absorb most volumetric attacks at the network edge before they reach your server.

Least Privilege

Run application processes with the minimum permissions necessary. Your web application shouldn't run as root. Database users should only have the permissions they need. File permissions should be as restrictive as possible.

Secrets Management

Store secrets (database passwords, API keys, credentials) in environment variables or secret management services, never in source code.

# Check for secrets accidentally committed
git log --all --full-history -- '*.env'

Use tools like git-secrets or trufflehog to scan repositories for accidentally committed credentials.


Security Monitoring

Security monitoring is the detection layer — identifying attacks and breaches after prevention has been bypassed.

Uptime Monitoring as Security Signal

Downtime can be a security incident. A DDoS attack takes your site down. A defacement attack might return unexpected content. Sudden spikes in error rates can indicate an active attack.

Domain Monitor monitors your site continuously and alerts you the moment it goes down, returns unexpected errors, or shows response time anomalies. This gives you visibility into the availability layer of security. Create a free account to set up monitoring.

Application Logging

Log security-relevant events:

  • Authentication attempts (success and failure)
  • Password resets
  • Permission changes
  • Unusual access patterns (many 404s from one IP, unusual parameter values)

Log the who, what, and when for every security-relevant event, with enough detail to investigate an incident.

Dependency Monitoring

Set up automated vulnerability alerts for your dependencies. GitHub Dependabot and security advisory subscriptions ensure you're notified when a dependency you use has a known vulnerability.


Security Checklist

A practical starting point for any web application:

Transport & Access

  • HTTPS configured with TLS 1.2/1.3
  • HTTP redirects to HTTPS
  • HSTS header set
  • SSL certificate expiry monitored

Headers

  • Content-Security-Policy configured
  • X-Frame-Options set
  • X-Content-Type-Options: nosniff
  • Referrer-Policy set

Input & Output

  • All database queries parameterised
  • All user output HTML-encoded in templates
  • CSRF protection on state-changing forms
  • Object ownership verified on every request

Authentication

  • Passwords hashed with bcrypt or Argon2
  • MFA available
  • Login rate limiting in place
  • Sessions use httpOnly and Secure cookies

Dependencies & Configuration

  • Dependencies up to date and vulnerability-scanned
  • Secrets in environment variables, not code
  • Verbose error messages disabled in production
  • Directory listing disabled

Monitoring

  • Uptime monitoring configured
  • Alerts on downtime and errors
  • Security-relevant events logged

For SSL certificate monitoring and uptime monitoring, Domain Monitor covers both automatically. See uptime monitoring best practices for the monitoring side of your security posture.

More posts

What Is Generative AI? How It Works and What It Creates

Generative AI creates new content — text, images, code, and more. This guide explains how it works, what tools are available, and where it's genuinely useful versus overhyped.

Read more
What Is Cursor AI? The AI Code Editor Explained

Cursor AI is an AI-powered code editor built on VS Code. Learn what it does, how it works, and whether it's the right tool for your development workflow.

Read more
What Is Claude Opus? Anthropic's Most Powerful Model Explained

Claude Opus is Anthropic's most capable AI model, built for complex reasoning and demanding tasks. Learn what it does, how it compares, and when to use it.

Read more

Subscribe to our PRO plan.

Looking to monitor your website and domains? Join our platform and start today.