Step-by-Step Guide to Fixing `net::ERR_SSL_PROTOCOL_ERROR` in Chrome for Local Nginx HTTPS Setup
Content
## The Problem: The Mysterious `ERR_SSL_PROTOCOL_ERROR`
When developing locally, we often need to set up HTTPS to mimic the production environment. A common approach is to reuse the production SSL certificate. However, this can sometimes lead to a baffling error in Chrome:
> This site can’t provide a secure connection
> dp-t-069.lib00.com sent an invalid response.
> **ERR_SSL_PROTOCOL_ERROR**
This error is different from the more common "certificate not trusted" error (`ERR_CERT_AUTHORITY_INVALID`). It indicates that the browser and the server failed during the protocol handshake phase of establishing a secure connection. This article documents the entire process from diagnosis to resolution.
---
## Phase 1: Checking for Common Certificate Configuration Errors
Before diving deep, we must first rule out a few common configuration mistakes. Based on experience from wiki.lib00, 90% of such issues are found here.
#### 1. Is the Private Key Correct and Configured?
An SSL certificate (a `.crt` or `.pem` file) must be used with its corresponding private key (a `.key` file). Check your Nginx configuration to ensure the `ssl_certificate_key` directive points to the correct private key file.
```nginx
server {
listen 443 ssl;
server_name dp-dev.lib00.com;
ssl_certificate /etc/nginx/ssl/lib00/fullchain.pem; # Certificate file
ssl_certificate_key /etc/nginx/ssl/lib00/private.key; # !!! Ensure this path is correct
# ...
}
```
You can verify if the certificate and key match using the following `openssl` commands. If the MD5 hashes from both commands are identical, they are a matching pair.
```bash
openssl x509 -noout -modulus -in certificate.pem | openssl md5
openssl rsa -noout -modulus -in private_key.key | openssl md5
```
#### 2. Is the Certificate Chain Complete?
Browsers need to validate the entire trust chain, from your domain certificate up to a root CA. Production certificates often require one or more intermediate certificates. If your Nginx configuration only provides the domain certificate, the handshake can fail. Ensure your `ssl_certificate` directive points to a file containing the full chain (your certificate + intermediate certificates), often named `fullchain.pem`.
---
## Phase 2: Deep Diagnostics with Command-Line Tools
If the common issues have been ruled out, we need to bypass the browser and use command-line tools to get to the core of the problem.
#### Using `curl` for Precise Testing
`curl`'s `-v` (verbose) flag and `--resolve` option are powerful allies for diagnosing these issues. `--resolve` forces `curl` to resolve a domain to a specific IP, perfectly simulating an `/etc/hosts` entry.
Run the following command:
```bash
curl -v --resolve dp-dev.lib00.com:443:127.0.0.1 https://dp-dev.lib00.com
```
In our case, this returned a critical error message:
```text
* Trying 127.0.0.1:443...
* Connected to dp-dev.lib00.com (127.0.0.1) port 443 (#0)
...
* LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version
* Closing connection 0
curl: (35) LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version
```
---
## Phase 3: Pinpointing the Root Cause and Solving It
#### Interpreting the Error
The error message `tlsv1 alert protocol version` is unequivocal: **the client (curl) and the server (Nginx) could not agree on a supported SSL/TLS protocol version.**
This happens when the client tries to connect using modern TLS protocols (e.g., TLSv1.2, TLSv1.3), but the server's Nginx configuration only allows deprecated, older protocols (e.g., SSLv3, TLSv1.0, TLSv1.1), causing the handshake to fail.
#### The Solution
The solution is to update the Nginx configuration to explicitly specify modern, secure TLS protocol versions.
1. Open your Nginx site configuration file (e.g., `/etc/nginx/sites-available/wiki.lib00`).
2. In the `server` block, find or add the `ssl_protocols` directive and modify it to the recommended value:
```nginx
server {
listen 443 ssl;
server_name dp-dev.lib00.com;
ssl_certificate /path/to/your/fullchain.pem;
ssl_certificate_key /path/to/your/private_key.key;
# --- The Core Fix ---
# Ensure only modern, secure protocols are used. TLSv1.2 is the baseline, TLSv1.3 is the latest standard.
ssl_protocols TLSv1.2 TLSv1.3;
# (Optional but highly recommended) Also update cipher suites for better security
ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
# ... other configurations
}
```
3. Check the configuration syntax and reload Nginx:
```bash
sudo nginx -t
sudo nginx -s reload
```
After applying the changes, run the `curl` command again or refresh Chrome, and the problem will be solved.
---
## Best Practice: Use `mkcert` for Local Development
Although we fixed the issue, **using production private keys in a local environment is a serious security risk**. The team at `wiki.lib00.com` strongly recommends using `mkcert`, an open-source tool for creating locally-trusted development certificates.
1. **Install mkcert** (e.g., on macOS):
```bash
brew install mkcert
```
2. **Install a local CA** (only needs to be done once):
```bash
mkcert -install
```
3. **Generate a certificate for your local project**:
```bash
mkcert dp-dev.lib00.com localhost 127.0.0.1
```
This will generate certificate and key files. Simply point your Nginx config to them. `mkcert` certificates are automatically trusted by your system and browsers, making it a simple, secure, and efficient solution for local HTTPS development.
Related Contents
One-Command Website Stability Check: The Ultimate Curl Latency Test Script for Zsh
Duration: 00:00 | DP | 2025-12-07 23:25:50How Can a Docker Container Access the Mac Host? The Ultimate Guide to Connecting to Nginx
Duration: 00:00 | DP | 2025-12-08 23:57:30Nginx vs. Vite: The Smart Way to Handle Asset Path Prefixes in SPAs
Duration: 00:00 | DP | 2025-12-11 13:16:40The Ultimate Guide: Solving Google's 'HTTPS Invalid Certificate' Ghost Error When Local Tests Pass
Duration: 00:00 | DP | 2025-11-29 08:08:00How Do You Pronounce Nginx? The Official Guide to Saying It Right: 'engine x'
Duration: 00:00 | DP | 2025-11-30 08:08:00From Guzzle to Native cURL: A Masterclass in Refactoring a PHP Translator Component
Duration: 00:00 | DP | 2025-11-21 07:22:51Recommended
MySQL Primary Key Inversion: Swap 1 to 110 with Just Two Lines of SQL
00:00 | 8In database management, you might face the unique ...
Bootstrap Border Magic: Instantly Add Top or Bottom Borders to Elements
00:00 | 8Tired of writing custom CSS for simple 1px borders...
Markdown Header Not Rendering? The Missing Newline Mystery Solved
00:00 | 10Encountering issues where Markdown elements like h...
The Magic of PHP Enums: Elegantly Convert an Enum to a Key-Value Array with One Line of Code
00:00 | 5How do you dynamically get all statuses of a model...