When developing web applications or APIs locally or on a staging server, you often need HTTPS to test features like Service Workers, Geolocation API, Clipboard API, WebCrypto, or mixed-content policies. Obtaining a real SSL certificate at this stage is overkill — a self-signed certificate generated with OpenSSL is the fastest, free alternative. This guide walks through every step: installing OpenSSL, understanding certificate file types, generating keys and certificates with Subject Alternative Names (SAN), configuring Apache and Nginx, and adding the certificate to your system trust store so the browser warning disappears entirely.
What Is a Self-Signed SSL Certificate and When Should You Use It?
A self-signed SSL certificate is an X.509 certificate that you generate and sign yourself using OpenSSL rather than submitting a Certificate Signing Request (CSR) to a trusted Certificate Authority (CA) like DigiCert, Sectigo, or Let's Encrypt. Because browsers don't recognise the issuing authority, they display security warnings such as "Your connection is not private" or NET::ERR_CERT_AUTHORITY_INVALID.
Despite this limitation, self-signed certificates are the right tool in several scenarios:
- Local development — Test HTTPS-only browser features (Service Workers, camera access, WebAuthn, Clipboard API, HTTP/2) on localhost without deploying to a real server.
- Staging and UAT environments — Internal servers accessed only by team members where you can manage trust policies centrally.
- Internal tooling — API gateways, dashboards, or admin panels accessible only within a private network or VPN.
- Testing TLS configuration — Validate TLS protocol versions, cipher suites, and HSTS headers on a web server before going live.
- Container networks — Mutual TLS (mTLS) between microservices within a Docker or Kubernetes network where all participants are trusted.
Important: Never use a self-signed certificate on a public-facing production server. Every major browser will block visitors with a full-page warning before they can proceed, severely damaging trust and conversion rates.
Understanding Certificate File Types
Before generating certificates, it helps to understand what each file type is used for:
| File / Extension | Description | Used by |
|---|---|---|
server.key |
Private Key — keep this secret, never share | Apache / Nginx web server |
server.csr |
Certificate Signing Request — sent to a CA (or self-signed) | CA submission or self-signing |
server.crt |
Certificate in PEM format — the public certificate | Web server + trust store import |
server.pem |
PEM format — identical to .crt, some tools require this extension | Linux CA store, Docker, Node.js |
server.pfx / .p12 |
PKCS#12 — bundles key + certificate in a single binary file | Windows IIS, .NET, Java KeyStore |
For Linux-based web servers running Apache or Nginx, you will primarily work with .key (private key) and .crt (certificate) files in PEM format — a Base64-encoded text format that begins with -----BEGIN CERTIFICATE-----.
Verify OpenSSL and Prepare Your Environment
OpenSSL comes pre-installed on most Linux distributions and macOS. Windows users must install it separately. Confirm availability with:
openssl version
Expected output:
OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
If OpenSSL is not installed, use the appropriate package manager:
# Ubuntu / Debian sudo apt-get update && sudo apt-get install -y openssl # CentOS / RHEL / Rocky Linux sudo dnf install -y openssl # macOS via Homebrew brew install openssl # Windows: download Win32/Win64 OpenSSL from https://slproweb.com/products/Win32OpenSSL.html # Or use WSL (Windows Subsystem for Linux) to run Linux commands directly.
Create a working directory for your certificate files:
mkdir -p ~/ssl-certs && cd ~/ssl-certs
Method 1 — Quick One-Command Certificate
The fastest approach combines key generation and certificate creation in a single command. This is ideal for a quick dev environment setup:
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes \ -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyCompany Dev/OU=Development/CN=localhost"
Parameter breakdown:
req -x509— create a self-signed certificate directly, bypassing a CA-newkey rsa:4096— generate a new 4096-bit RSA private key-keyout server.key— write the private key to server.key-out server.crt— write the certificate to server.crt-days 365— certificate valid for 365 days-nodes— do not encrypt the private key ("no DES"), so the web server can start without prompting for a passphrase-subj— provide subject details non-interactively
After running, you will have two files: server.key (private key) and server.crt (certificate), both ready to configure in a web server.
Method 2 — Certificate with Subject Alternative Names (SAN)
Modern browsers require a certificate to include Subject Alternative Names (SAN) or they will show an extra warning — "Subject Alternative Name Missing" — even if you have already accepted the certificate. The following approach creates a certificate covering multiple hostnames and IP addresses using a configuration file:
# Create SAN configuration file cat > san.cnf << 'EOF' [req] default_bits = 4096 prompt = no default_md = sha256 distinguished_name = dn x509_extensions = v3_req [dn] C = TH ST = Bangkok L = Bangkok O = MyCompany Development CN = localhost [v3_req] subjectAltName = @alt_names keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [alt_names] DNS.1 = localhost DNS.2 = *.localhost DNS.3 = dev.local DNS.4 = *.dev.local DNS.5 = staging.myapp.com IP.1 = 127.0.0.1 IP.2 = 192.168.1.100 EOF # Generate certificate from config openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes -config san.cnf
This single certificate covers localhost, *.localhost, dev.local, *.dev.local, staging.myapp.com, and the specified IP addresses — eliminating the need to generate a separate certificate for each hostname in your dev environment.
Method 3 — Step-by-Step Certificate Generation
For greater control or when you need to retain the CSR for submission to an internal CA, split the process into three explicit steps:
Step 1: Generate a Private Key
# RSA 4096-bit (recommended) openssl genrsa -out server.key 4096 # Alternative: ECDSA P-256 (faster key generation and TLS handshake) openssl ecparam -genkey -name prime256v1 -out server.key
Step 2: Create a CSR
openssl req -new -key server.key -out server.csr \ -subj "/C=TH/ST=Bangkok/L=Bangkok/O=MyOrg/CN=localhost"
Step 3: Self-Sign the CSR
openssl x509 -req -in server.csr -signkey server.key \ -out server.crt -days 365 \ -extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1")
Keeping the CSR (server.csr) is useful if you later decide to have it signed by your organisation's internal CA rather than self-signing.
Verify Your Certificate
Always inspect the generated certificate before deploying it to confirm the details are correct:
# View full certificate details openssl x509 -in server.crt -text -noout # View subject, issuer, and validity dates only openssl x509 -in server.crt -subject -issuer -dates -noout # Confirm private key matches certificate (MD5 hashes must be identical) openssl x509 -noout -modulus -in server.crt | md5sum openssl rsa -noout -modulus -in server.key | md5sum # Confirm SAN entries openssl x509 -in server.crt -text -noout | grep -A2 "Subject Alternative Name"
Key fields to verify in the output:
- Validity — Correct Not Before and Not After dates
- Subject CN — Matches your intended hostname
- X509v3 Subject Alternative Name — All required DNS names and IPs are listed
- Public Key Algorithm — RSA 4096-bit or EC prime256v1
Configure Your Web Server
Copy the certificate files to a standard system location, then configure your web server to use them. The examples below assume certificates are stored in /etc/ssl/self-signed/:
# Copy certificate files sudo mkdir -p /etc/ssl/self-signed sudo cp server.key /etc/ssl/self-signed/server.key sudo cp server.crt /etc/ssl/self-signed/server.crt sudo chmod 600 /etc/ssl/self-signed/server.key sudo chmod 644 /etc/ssl/self-signed/server.crt
Nginx Configuration
server {
listen 80;
server_name localhost dev.local;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name localhost dev.local;
ssl_certificate /etc/ssl/self-signed/server.crt;
ssl_certificate_key /etc/ssl/self-signed/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
root /var/www/html;
index index.html index.php;
location / {
try_files $uri $uri/ =404;
}
}
Apache Configuration
<VirtualHost *:443>
ServerName localhost
ServerAlias dev.local
SSLEngine on
SSLCertificateFile /etc/ssl/self-signed/server.crt
SSLCertificateKeyFile /etc/ssl/self-signed/server.key
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite HIGH:!aNULL:!MD5
DocumentRoot /var/www/html
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName localhost
Redirect permanent / https://localhost/
</VirtualHost>
Test and reload your web server after making changes:
# Nginx sudo nginx -t && sudo systemctl reload nginx # Apache sudo apachectl configtest && sudo systemctl reload apache2
Adding the Certificate to Your System Trust Store
To eliminate browser warnings entirely in your dev environment, import the certificate into your operating system's trust store. This must be done on each developer machine individually.
macOS
# Via command line sudo security add-trusted-cert -d -r trustRoot \ -k /Library/Keychains/System.keychain server.crt # Via Keychain Access GUI: # 1. Double-click server.crt to open Keychain Access # 2. Find the certificate entry — double-click it # 3. Expand Trust → "When using this certificate" → Always Trust
Linux — Ubuntu / Debian
sudo cp server.crt /usr/local/share/ca-certificates/dev-localhost.crt sudo update-ca-certificates # Output: 1 added, 0 removed; done.
Windows
# PowerShell (run as Administrator) Import-Certificate -FilePath "C:\path\to\server.crt" ` -CertStoreLocation Cert:\LocalMachine\Root # Or via GUI: # 1. Double-click server.crt → Install Certificate # 2. Store Location: Local Machine → Next # 3. Place all certificates in: Trusted Root Certification Authorities → OK → Finish
Pro tip — mkcert for teams: If your team generates self-signed certificates frequently, consider mkcert (github.com/FiloSottile/mkcert). It creates a local CA automatically and installs it into the system trust store of every OS with a single command: mkcert -install. Then generate a trusted certificate with mkcert localhost 127.0.0.1 ::1 — no OpenSSL configuration files required. Distribute the CA certificate (rootCA.pem) to all team members once, and every subsequent certificate generated by mkcert will be trusted automatically.
Using Self-Signed SSL with Docker and Node.js
Self-signed certificates are frequently needed in containerised development environments. Here are the most common patterns:
Node.js HTTPS Server
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('/path/to/server.key'),
cert: fs.readFileSync('/path/to/server.crt'),
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('Hello HTTPS\n');
}).listen(443, () => console.log('HTTPS server running on :443'));
Docker Compose with Nginx + SSL
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./ssl/server.crt:/etc/ssl/certs/server.crt:ro
- ./ssl/server.key:/etc/ssl/private/server.key:ro
- ./html:/usr/share/nginx/html:ro
Disabling SSL Verification in Development Tools
Some tools reject self-signed certificates by default. Use these flags in development only — always re-enable certificate verification in production environments:
# curl
curl -k https://localhost/api/health
# wget
wget --no-check-certificate https://localhost/file.zip
# Python requests
import requests
response = requests.get('https://localhost', verify=False)
# Node.js environment variable
export NODE_TLS_REJECT_UNAUTHORIZED=0
# Git (set per-repository, not globally)
git -c http.sslVerify=false clone https://localhost/repo.git
Creating a Private Certificate Authority for Your Team
If your team frequently works with internal HTTPS services, a better long-term solution is to create a private Root CA, distribute its certificate to every team member once, and then sign all server certificates with it. New certificates will be automatically trusted without any additional imports.
# Step 1: Create the Root CA private key openssl genrsa -out ca.key 4096 # Step 2: Create the Root CA certificate (10-year validity) openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \ -out ca.crt \ -subj "/C=TH/ST=Bangkok/O=MyCompany Dev CA/CN=MyCompany Development Root CA" # Step 3: Create the server private key openssl genrsa -out server.key 4096 # Step 4: Create the server CSR openssl req -new -key server.key -out server.csr \ -subj "/C=TH/ST=Bangkok/O=MyCompany/CN=dev.mycompany.local" # Step 5: Sign the server CSR with the CA openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \ -CAcreateserial -out server.crt -days 365 -sha256 \ -extfile <(printf "subjectAltName=DNS:dev.mycompany.local,DNS:*.dev.mycompany.local,IP:192.168.1.100")
Distribute ca.crt to every team member to install in their trust store once. All server certificates subsequently signed by this CA will be trusted automatically — no additional per-certificate imports needed.
Renewing Self-Signed Certificates
Unlike CA-issued certificates, self-signed certificates have no renewal process — you simply generate a new one when the old one expires or when subject information changes. Check expiry with:
# Check expiry date openssl x509 -in server.crt -noout -enddate # notAfter=Jun 9 00:00:00 2027 GMT # Regenerate using saved config file openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt \ -days 365 -nodes -config san.cnf # Reload web server after renewal sudo systemctl reload nginx
For automated environments, add this command to a cron job that runs 30 days before the certificate expires, followed by a web server reload.
Frequently Asked Questions
What is the difference between a self-signed SSL and a CA-issued certificate?
A self-signed SSL certificate is generated and signed by you using OpenSSL — no Certificate Authority is involved. Browsers do not recognise the issuing authority, so they display "Your connection is not private" warnings. CA-issued certificates (from DigiCert, Let's Encrypt, etc.) are trusted by browsers worldwide. Use self-signed certificates only for development, staging, or internal tools where you control who accesses the service.
Can I use a self-signed SSL on a production server?
It is strongly discouraged. Every major browser — Chrome, Firefox, Safari, and Edge — will display a full-page "Not Secure" warning before visitors can reach your site. This destroys user trust and conversion rates. API clients also reject self-signed certificates by default unless you explicitly disable verification. For production, use a trusted CA certificate or the free Let's Encrypt service.
How do I make my browser trust a self-signed SSL certificate?
Import the .crt file into your operating system's trust store. On macOS, use Keychain Access or sudo security add-trusted-cert. On Windows, use certmgr.msc → Trusted Root Certification Authorities → Import. On Linux (Ubuntu/Debian), copy to /usr/local/share/ca-certificates/ and run sudo update-ca-certificates. This works only on machines you control — it cannot make external users trust the certificate.
How long is a self-signed SSL certificate valid?
You set the validity period yourself with the -days parameter. Common values are 365 (one year) or 3650 (ten years) for dev environments. However, modern browsers — especially Safari on iOS and macOS — enforce a maximum certificate lifetime of approximately 825 days. Certificates with longer validity periods may trigger additional security warnings. As a best practice, regenerate your certificate annually or whenever the Subject information changes.
SSL Certificates for Every Use Case at AsiaGB
DV, OV, EV, and Wildcard SSL certificates starting from 1,000 THB/year with full installation support included.
View SSL Certificates