How I Fixed SSL Certificate Issues on My Website A Complete Guide

Table Of Contents
The Problem That Started It All
Picture this: I had just installed an SSL certificate on my website, shadynagy.com. Everything looked great on my end—the certificate was valid, issued by Sectigo, and had plenty of time before expiration (265 days!). But when I ran a quick SSL test at WhyNoPadlock.com, I got hit with some disappointing red X marks:
❌ Force HTTPS: Not forcing the use of SSL
❌ Invalid Intermediate: Missing intermediate (bundle) certificate
My heart sank. What good is an SSL certificate if it’s not working properly? My visitors might see security warnings, search engines might rank me lower, and worst of all—my site could look unprofessional.
But here’s the good news: I fixed everything, and I’m going to show you exactly how I did it, step by step.
Understanding What Went Wrong
Before diving into solutions, let’s understand what these errors actually mean:
Problem #1: Missing Intermediate Certificate
What’s an intermediate certificate?
Think of SSL certificates like a chain of trust:
- Your browser trusts certain “root” certificate authorities (like Sectigo)
- These authorities issue “intermediate” certificates to sub-authorities
- These sub-authorities issue your website’s certificate
Your website needs to send BOTH your certificate AND the intermediate certificate to browsers. Without the intermediate, the browser can’t verify the chain of trust.
Real-world impact:
- Some browsers show security warnings
- Mobile devices often fail to connect
- Your site looks unsafe to visitors
Problem #2: No HTTP to HTTPS Redirect
What does this mean?
When someone types http://shadynagy.com (without the ‘s’), they should automatically be redirected to https://shadynagy.com. Without this redirect:
- Visitors can access your site over insecure HTTP
- Search engines see duplicate content (HTTP and HTTPS versions)
- You lose the SEO benefits of HTTPS
Solution #1: Fixing the Missing Intermediate Certificate
Step 1: Understanding the Root Cause
I discovered my nginx configuration was using the wrong directive. Here’s what I had:
# ❌ WRONG CONFIGURATIONssl_certificate /etc/nginx/ssl/shadynagy.com.crt;ssl_trusted_certificate /etc/nginx/ssl/shadynagy.com.ca-bundle;
The problem: ssl_trusted_certificate is for OCSP stapling (a performance feature), NOT for sending the certificate chain to browsers!
Step 2: Creating the Full Chain Certificate
The solution is to create a “fullchain” certificate that combines your certificate with the intermediate certificate.
Command:
cat /etc/nginx/ssl/shadynagy.com.crt /etc/nginx/ssl/shadynagy.com.ca-bundle > /etc/nginx/ssl/shadynagy.com-fullchain.crt
What this does:
cat- Concatenates (combines) files- First file: Your website’s certificate
- Second file: The intermediate CA bundle
>- Outputs to a new file called “fullchain”
Example explanation: Imagine you have two puzzle pieces:
- Your certificate (Piece A)
- The intermediate certificate (Piece B)
You’re gluing them together to create one complete puzzle that browsers can understand.
Step 3: Updating Nginx Configuration
Open your SSL configuration file:
nano /etc/nginx/conf.d/shadynagy.com-ssl.conf
Update to this:
server {listen 443 ssl http2; # Port 443 with SSL and HTTP/2# ✅ CORRECT - Use the fullchain certificatessl_certificate /etc/nginx/ssl/shadynagy.com-fullchain.crt;ssl_certificate_key /etc/nginx/ssl/shadynagy.com.key;# Optional: Keep for OCSP stapling (performance boost)ssl_trusted_certificate /etc/nginx/ssl/shadynagy.com.ca-bundle;root /var/www/shady-nagy.com/html;index index.html index.htm;server_name shadynagy.com www.shadynagy.com;access_log /var/log/nginx/nginx.vhost.access.log;error_log /var/log/nginx/nginx.vhost.error.log;location / {try_files $uri $uri/ =404;}}
Key changes explained:
listen 443 ssl http2;443= HTTPS portssl= Enable SSLhttp2= Enable HTTP/2 for better performance- This replaces the deprecated
ssl on;directive
ssl_certificatenow points to fullchain instead of just your certificatessl_trusted_certificateis kept for OCSP stapling (optional but recommended)
Step 4: Test and Apply Changes
Test the configuration:
nginx -t
Expected output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is oknginx: configuration file /etc/nginx/nginx.conf test is successful
If you see any errors, double-check your file paths and syntax!
Apply the changes:
systemctl reload nginx
Why reload instead of restart?
reloadapplies changes without dropping connectionsrestartwould briefly take your site offline
✅ Benefits of This Fix
- ✅ All browsers can verify your SSL certificate
- ✅ No more “Invalid Certificate” warnings
- ✅ Mobile devices connect properly
- ✅ Better trust and SEO rankings
- ✅ Professional appearance
Solution #2: Forcing HTTPS Redirects
The Investigation Journey
First, I checked my HTTP configuration:
cat /etc/nginx/conf.d/shadynagy.com.conf
I found this:
# ❌ BROKEN CONFIGURATIONserver {listen 80;root /var/www/shady-nagy.com/html;server_name shadynagy.com www.shadynagy.com;location / {try_files $uri $uri/ =404;}return 301 https://$server_name$request_uri; # Never reached!}
The problem: Nginx reads configurations top to bottom. When a request came in:
- It matched the
location /block - Processed the request
- NEVER reached the redirect line
It’s like putting a “Detour” sign AFTER the road!
Step 1: Creating the Correct Redirect Configuration
The fix:
# ✅ CORRECT CONFIGURATIONserver {listen 80;listen [::]:80; # Also listen on IPv6server_name shadynagy.com www.shadynagy.com;# Redirect ALL HTTP traffic to HTTPSreturn 301 https://$host$request_uri;}
What each part does:
listen 80;- Listen for HTTP requests (port 80)listen [::]:80;- Also listen for IPv6 HTTP requestsserver_name- Which domains this applies toreturn 301- Send a permanent redirect (301 status code)$host- The domain the user typed (preserves www vs non-www)$request_uri- The path they requested (e.g., /about or /contact)
Example in action:
If someone visits: http://www.shadynagy.com/how-i-fixed-ssl-certificate-issues-on-my-website-a-complete-guide
They’re redirected to: https://www.shadynagy.com/how-i-fixed-ssl-certificate-issues-on-my-website-a-complete-guide
Step 2: Apply and Test
Test the configuration:
nginx -t
Reload nginx:
systemctl reload nginx
Test the redirect:
curl -I http://shadynagy.com
Expected output:
HTTP/1.1 301 Moved PermanentlyLocation: https://shadynagy.com/
Perfect! But wait…
The Plot Twist: The Firewall Was Blocking Port 80!
When I tested in my browser, it still didn’t work! After some investigation:
firewall-cmd --list-all
Output:
services: https
Notice what’s missing? HTTP (port 80)!
The redirect was configured perfectly, but my firewall was blocking incoming HTTP traffic. It’s like installing a front door but building a wall in front of it!
Step 3: Opening the Firewall
Add HTTP service:
firewall-cmd --permanent --add-service=http
Reload firewall:
firewall-cmd --reload
Verify it worked:
firewall-cmd --list-all
Now you should see:
services: http https
✅ Benefits of This Fix
- ✅ All visitors automatically use HTTPS
- ✅ No duplicate content issues for SEO
- ✅ Better security for your visitors
- ✅ Improved search engine rankings
- ✅ Green padlock in browsers
- ✅ Builds trust with visitors
Complete Diagnostic Commands
Here are all the commands I used during troubleshooting:
Check Nginx Configuration
# Find all server blocks listening on port 80grep -r "listen 80" /etc/nginx/# Test nginx configuration syntaxnginx -t# Show complete nginx configurationnginx -T# View specific server blocknginx -T 2>/dev/null | grep -A 15 "server_name shadynagy.com"
Check Nginx Status
# Is nginx running?systemctl status nginx# Reload nginx (apply changes without downtime)systemctl reload nginx# Restart nginx (brief downtime)systemctl restart nginx# Check error logstail -20 /var/log/nginx/error.log
Test HTTP to HTTPS Redirect
# Test redirect (bypasses browser cache)curl -I http://shadynagy.com# Follow all redirectscurl -IL http://shadynagy.com
Check Port Status
# Is nginx listening on port 80?ss -tlnp | grep :80# Is nginx listening on port 443?ss -tlnp | grep :443
Check Firewall
# List all firewall rulesfirewall-cmd --list-all# Add HTTP permanentlyfirewall-cmd --permanent --add-service=http# Add HTTPS permanentlyfirewall-cmd --permanent --add-service=https# Apply changesfirewall-cmd --reload
Final Configuration Files
HTTP Configuration (Port 80)
File: /etc/nginx/conf.d/shadynagy.com.conf
server {listen 80;listen [::]:80;server_name shadynagy.com www.shadynagy.com;return 301 https://$host$request_uri;}
HTTPS Configuration (Port 443)
File: /etc/nginx/conf.d/shadynagy.com-ssl.conf
server {listen 443 ssl http2;ssl_certificate /etc/nginx/ssl/shadynagy.com-fullchain.crt;ssl_certificate_key /etc/nginx/ssl/shadynagy.com.key;ssl_trusted_certificate /etc/nginx/ssl/shadynagy.com.ca-bundle;root /var/www/shady-nagy.com/html;index index.html index.htm index.nginx-debian.html;server_name shadynagy.com www.shadynagy.com;access_log /var/log/nginx/nginx.vhost.access.log;error_log /var/log/nginx/nginx.vhost.error.log;location / {add_header Cache-Control "no-cache, no-store, must-revalidate";add_header Pragma "no-cache";add_header Expires 0;try_files $uri $uri/ =404;}}
The Results: Everything Fixed! ✅
After implementing all fixes, my SSL test at WhyNoPadlock.com showed:
✅ SSL Connection - Pass
✅ Valid Certificate - SSL Certificate is installed correctly
✅ Force HTTPS - Webserver is forcing the use of SSL
✅ Domain Matching - Certificate matches domain name
✅ Signature - Using sha256WithRSAEncryption
✅ Expiration Date - Certificate is current (expires 2026-11-03)
✅ Mixed Content - No mixed content
Perfect score!
Questions & Answers
Q1: Why do I need an intermediate certificate?
A: The intermediate certificate creates a “chain of trust” from your website to a trusted root authority. Without it:
- Browsers can’t verify your certificate is legitimate
- Visitors see scary security warnings
- Mobile devices often fail to connect
- Your site looks unprofessional and unsafe
Think of it like this: If someone introduces themselves as “John, friend of Sarah,” you might not trust them. But if Sarah is your best friend and she vouches for John, you trust him. The intermediate certificate is Sarah vouching for your website’s certificate.
Q2: What’s the difference between ssl_certificate and ssl_trusted_certificate in nginx?
A: Great question!
ssl_certificate: This is what nginx sends to browsers to prove your website’s identity. It should contain your certificate AND the intermediate certificate (fullchain).ssl_trusted_certificate: This is used for OCSP stapling, a performance feature. Nginx uses this to verify certificate revocation status on behalf of clients. It doesn’t get sent to browsers.
Analogy:
ssl_certificate= Your ID card you show to peoplessl_trusted_certificate= Your personal copy of the rules you reference for yourself
Q3: Why use $host instead of $server_name in redirects?
A:
$host: The exact domain the user typed in their browser (e.g., www.shadynagy.com or shadynagy.com)$server_name: The first server_name in your configuration
Example scenario:
- Your config has:
server_name shadynagy.com www.shadynagy.com; - User visits:
http://www.shadynagy.com
With $server_name: Redirects to https://shadynagy.com (drops the www)
With $host: Redirects to https://www.shadynagy.com (preserves the www)
Using $host respects what the user typed and prevents unnecessary additional redirects.
Q4: Why did the redirect work with curl but not in my browser?
A: This is usually one of three issues:
Firewall blocking port 80 (my problem!) - The redirect configuration was correct, but incoming HTTP traffic never reached nginx.
Browser cache - Browsers cache redirects aggressively. Solution: Test in incognito mode or clear cache.
DNS cache - Your computer might have old DNS records. Solution: Flush DNS cache or wait a few hours.
Pro tip: Always test with curl -I first because it bypasses all caching!
Q5: Should I use return or rewrite for HTTPS redirects?
A: Always use return for redirects!
return (recommended):
return 301 https://$host$request_uri;
- Faster (nginx stops processing immediately)
- Clearer intent
- Less error-prone
rewrite (avoid for simple redirects):
rewrite ^ https://$host$request_uri permanent;
- Slower (uses regex engine)
- More complex
- Easy to create infinite loops
Rule of thumb: Use return for redirects, use rewrite only when you need to modify the URL structure.
Q6: What’s the difference between reload and restart for nginx?
A:
systemctl reload nginx (recommended):
- Applies configuration changes gracefully
- Keeps existing connections alive
- No downtime
- Tests config before applying (won’t reload if config is broken)
systemctl restart nginx (use sparingly):
- Stops nginx completely, then starts it again
- Drops all active connections
- Brief downtime (usually milliseconds to seconds)
- Useful when reload doesn’t work
When to restart instead of reload:
- When adding new modules
- When reload fails to pick up changes
- When debugging weird issues
Q7: How do I know if my firewall is blocking traffic?
A: Run these diagnostic commands:
# Check if nginx is listening on port 80ss -tlnp | grep :80# If you see nginx here, it's listening# Check firewall rulesfirewall-cmd --list-all# Look for 'http' in the services list# Test from outside your servercurl -I http://your-domain.com# If it times out, firewall might be blocking
If nginx is listening BUT curl from outside times out → firewall is the problem!
Q8: My SSL test shows “Mixed Content” warnings. What does this mean?
A: Mixed content happens when your HTTPS page loads resources (images, scripts, CSS) over HTTP.
Example problem:
<!-- ❌ BAD - Loading image over HTTP on HTTPS page --><img src="http://shadynagy.com/image.jpg">
Solutions:
<!-- ✅ GOOD - Use HTTPS --><img src="https://shadynagy.com/image.jpg"><!-- ✅ GOOD - Use protocol-relative URL --><img src="//shadynagy.com/image.jpg"><!-- ✅ BEST - Use relative URL --><img src="/image.jpg">
Check for mixed content:
- Open your website in Chrome
- Press F12 (Developer Tools)
- Look for warnings in the Console tab
Q9: Do I need to do this every time I renew my SSL certificate?
A: Partially, yes.
What you need to do again:
- Create a new fullchain certificate (combining new cert + intermediate)
- Replace the old fullchain file
What you DON’T need to do again:
- Modify nginx configuration files (they’ll reference the same paths)
- Change firewall rules
- Set up redirects
Pro tip: Automate certificate renewal with Let’s Encrypt/Certbot, which handles the fullchain automatically!
Q10: Can I test my SSL configuration before making it live?
A: Absolutely! Here’s how:
1. Test nginx configuration syntax:
nginx -t
2. Test locally with curl:
# Test redirectcurl -I http://localhost# Test HTTPS (might get certificate warning if testing locally)curl -Ik https://localhost
3. Use online tools:
- SSL Labs - Comprehensive SSL test
- WhyNoPadlock - Quick SSL check
- SSL Shopper - Certificate chain checker
4. Test in staging environment: If possible, set up a test subdomain (like staging.shadynagy.com) and test there first.
Q11: What’s HTTP/2 and why did you add it?
A: HTTP/2 is a newer, faster version of the HTTP protocol.
Benefits:
- ✅ Multiplexing: Multiple files download simultaneously over one connection
- ✅ Header compression: Smaller requests/responses
- ✅ Server push: Server can send files before browser asks
- ✅ Better mobile performance
How to enable:
listen 443 ssl http2; # Just add 'http2' here!
Requirements:
- HTTPS must be enabled (HTTP/2 requires SSL)
- Nginx 1.9.5 or later
- OpenSSL 1.0.2 or later
Verification:
curl -I --http2 https://shadynagy.com# Look for "HTTP/2 200" in the response
Q12: What happens if I forget to make my firewall rules permanent?
A: If you don’t use --permanent, your firewall rules will disappear when you reboot the server!
Non-permanent (lost on reboot):
firewall-cmd --add-service=http # ❌ Gone after reboot
Permanent (survives reboot):
firewall-cmd --permanent --add-service=http # ✅ Stays after rebootfirewall-cmd --reload # Apply immediately
Check if a rule is permanent:
# Show runtime (current) rulesfirewall-cmd --list-all# Show permanent (saved) rulesfirewall-cmd --permanent --list-all
If they don’t match, you need to make your changes permanent!
Q13: How often should I check my SSL certificate?
A: Here’s a good maintenance schedule:
Monthly:
- Check certificate expiration date
- Run a quick SSL test at WhyNoPadlock.com
Before expiration:
- Renew certificate 30 days before it expires
- Test the new certificate immediately
- Set up automated renewal if possible
After server updates:
- Test SSL after any nginx/OpenSSL updates
- Verify redirects still work
Set up monitoring: Many services offer free SSL monitoring:
- UptimeRobot
- Pingdom
- SSL Labs monitoring
They’ll email you before your certificate expires!
Q14: What if I have multiple domains on one server?
A: You need a separate server block for each domain.
Example for multiple domains:
# Domain 1: shadynagy.comserver {listen 80;server_name shadynagy.com www.shadynagy.com;return 301 https://$host$request_uri;}server {listen 443 ssl http2;server_name shadynagy.com www.shadynagy.com;ssl_certificate /etc/nginx/ssl/shadynagy.com-fullchain.crt;ssl_certificate_key /etc/nginx/ssl/shadynagy.com.key;root /var/www/shadynagy.com;}# Domain 2: myotherdomain.comserver {listen 80;server_name myotherdomain.com www.myotherdomain.com;return 301 https://$host$request_uri;}server {listen 443 ssl http2;server_name myotherdomain.com www.myotherdomain.com;ssl_certificate /etc/nginx/ssl/myotherdomain.com-fullchain.crt;ssl_certificate_key /etc/nginx/ssl/myotherdomain.com.key;root /var/www/myotherdomain.com;}
Alternative: Use a wildcard certificate or multi-domain (SAN) certificate to cover multiple domains with one certificate.
Q15: Why is my certificate showing as “not secure” even after all these fixes?
A: Check these common issues:
Certificate name mismatch:
- Certificate is for
www.shadynagy.combut you’re visitingshadynagy.com - Solution: Get a certificate that covers both (SAN certificate)
- Certificate is for
Expired certificate:
- Check expiration date:
openssl x509 -in certificate.crt -noout -dates - Solution: Renew certificate
- Check expiration date:
Mixed content:
- HTTPS page loading HTTP resources
- Check browser console for warnings
Wrong domain in nginx config:
server_namedoesn’t match the domain you’re visiting- Solution: Add all domain variations to
server_name
Certificate not trusted:
- Using self-signed certificate
- Solution: Get certificate from trusted CA (Let’s Encrypt is free!)
Quick diagnosis:
# Check what certificate is being servedopenssl s_client -connect shadynagy.com:443 -servername shadynagy.com
Useful Online SSL Testing Tools
🔗 SSL Labs - Most comprehensive SSL test, gives you an A-F grade
🔗 WhyNoPadlock - Quick visual check for common SSL issues
🔗 SSL Shopper - Checks certificate installation and chain
🔗 Security Headers - Tests HTTP security headers
🔗 High-Tech Bridge - Detailed SSL/TLS and security assessment
Key Takeaways
For Intermediate Certificates:
✅ Always create a fullchain certificate (cert + CA bundle)
✅ Use ssl_certificate directive for the fullchain
✅ Use ssl_trusted_certificate only for OCSP stapling
✅ Test with online tools after installation
For HTTPS Redirects:
✅ Use return 301 for redirects (not rewrite)
✅ Place redirect BEFORE location blocks
✅ Use $host to preserve user’s domain input
✅ Remember to open port 80 in the firewall!
For Testing:
✅ Always test nginx config with nginx -t before reloading
✅ Use curl to bypass browser cache
✅ Check firewall rules with firewall-cmd --list-all
✅ Verify port listening with ss -tlnp
For Maintenance:
✅ Set up certificate expiration alerts
✅ Make firewall rules permanent
✅ Keep nginx and OpenSSL updated
✅ Regularly test SSL configuration
Final Thoughts
SSL configuration might seem daunting at first, but once you understand the pieces, it’s quite logical:
- Certificate chain - Prove your identity
- HTTPS redirect - Force secure connections
- Firewall rules - Allow traffic to reach your server
Breaking down the problem into these components made it much easier to diagnose and fix.
The most important lesson? When something doesn’t work, check layer by layer:
- Is nginx configured correctly? (
nginx -t) - Is nginx listening? (
ss -tlnp) - Is the firewall allowing traffic? (
firewall-cmd --list-all) - Does it work locally? (
curl -I http://localhost)
This systematic approach saved me hours of frustration!
Feedback and Questions
We’d love to hear your feedback on this tutorial! If you have any questions or suggestions for improvement, please don’t hesitate to reach out. You can leave a comment below, or you can contact us through the following channels:
- Email: info@shadynagy.com
- Twitter: @ShadyNagy_
- LinkedIn: Shady Nagy
- GitHub: ShadyNagy
Found this helpful? Share it with someone struggling with SSL configuration!







