Skip to content

Commit edcb0b7

Browse files
authored
SEO improvements: meta tags, schema.org, nginx cache headers, and security headers (#48)
* SEO improvements: meta tags, schema.org, nginx cache headers, and security headers * feat: replace text header with logo image and update brand layout styling * feat: implement centralized security headers and optimize SEO metadata and caching configuration
1 parent 232556e commit edcb0b7

9 files changed

Lines changed: 144 additions & 34 deletions

File tree

.env.sample

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ CLEANUP_SCANNED_IMAGES=true
2222
# SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
2323
SLACK_WEBHOOK_URL=
2424

25-
26-
# Google Analytics Tag ID (gtag.js)
27-
# Example: G-XXXXXXXXXX
28-
GOOGLE_TAG_ID=
25+
# Google Analytics Tag ID (gtag.js)
26+
# Example: G-XXXXXXXXXX
27+
GOOGLE_TAG_ID=
28+
29+
# Site Domain (used for SEO meta tags, canonical URLs, og:tags)
30+
# Default: https://infrascan.soldevelo.com
31+
SITE_DOMAIN=https://infrascan.soldevelo.com

app.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def inject_global_vars():
4343
"""Make global variables available in all templates."""
4444
return {
4545
'static_version': STATIC_VERSION,
46-
'google_tag_id': os.getenv('GOOGLE_TAG_ID', '')
46+
'google_tag_id': os.getenv('GOOGLE_TAG_ID', ''),
47+
'site_domain': os.getenv('SITE_DOMAIN', 'https://infrascan.soldevelo.com')
4748
}
4849

4950
def get_slack_webhook_url() -> str:

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ services:
3333
- "443:443"
3434
volumes:
3535
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
36+
- ./nginx/security_headers.inc:/etc/nginx/security_headers.inc:ro
3637
- ./static:/opt/infrascan/static:ro
3738
- ./certbot/conf:/etc/letsencrypt:ro
3839
- ./certbot/www:/var/www/certbot:ro

nginx/nginx.conf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ server {
2020
ssl_protocols TLSv1.2 TLSv1.3;
2121
ssl_prefer_server_ciphers on;
2222
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
23+
24+
# Security headers for SEO
25+
include /etc/nginx/security_headers.inc;
2326

2427
client_max_body_size 10M;
2528

@@ -32,18 +35,34 @@ server {
3235
proxy_read_timeout 600s;
3336
proxy_connect_timeout 600s;
3437
proxy_send_timeout 600s;
38+
39+
# Cache headers for HTML (no-cache to get fresh content, but browser can revalidate)
40+
add_header Cache-Control "no-cache" always;
41+
include /etc/nginx/security_headers.inc;
3542
}
3643

3744
location /static/ {
3845
alias /opt/infrascan/static/;
46+
# Cache static assets for 1 year
47+
expires 365d;
48+
add_header Cache-Control "public, max-age=31536000, immutable" always;
49+
include /etc/nginx/security_headers.inc;
3950
}
4051

4152
location = /sitemap.xml {
4253
alias /opt/infrascan/static/sitemap.xml;
54+
# Cache sitemap for 24 hours
55+
add_header Cache-Control "public, max-age=86400" always;
56+
add_header Content-Type "application/xml; charset=utf-8" always;
57+
include /etc/nginx/security_headers.inc;
4358
}
4459

4560
location = /robots.txt {
4661
alias /opt/infrascan/static/robots.txt;
62+
# Cache robots.txt for 24 hours
63+
add_header Cache-Control "public, max-age=86400" always;
64+
add_header Content-Type "text/plain; charset=utf-8" always;
65+
include /etc/nginx/security_headers.inc;
4766
}
4867

4968
# Error pages

nginx/security_headers.inc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
2+
add_header X-Content-Type-Options "nosniff" always;
3+
add_header X-Frame-Options "SAMEORIGIN" always;
4+
add_header X-XSS-Protection "1; mode=block" always;
5+
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https://img.shields.io https://www.googletagmanager.com https://www.google-analytics.com; connect-src 'self' https://www.google-analytics.com; frame-ancestors 'self'; object-src 'none';" always;
6+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

static/images/logo_transparent.png

188 KB
Loading

static/robots.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
User-agent: *
22
Allow: /
3+
Allow: /api/
34

45
Sitemap: https://infrascan.soldevelo.com/sitemap.xml
6+

static/style.css

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,34 @@ header {
5353

5454
.logo {
5555
display: flex;
56+
flex-direction: column;
5657
align-items: center;
57-
/* Back to center for better overall feel */
5858
justify-content: center;
5959
gap: 0.75rem;
60-
margin-bottom: 1.25rem;
60+
margin-bottom: 2rem;
61+
}
62+
63+
.main-brand-row {
64+
display: flex;
65+
align-items: center;
66+
justify-content: center;
67+
gap: 0;
68+
}
69+
70+
.main-logo {
71+
width: auto;
72+
height: 110px; /* Slightly refined size for better symmetry */
73+
max-width: 100%;
74+
display: block;
75+
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), filter 0.3s ease;
76+
filter: drop-shadow(0 8px 16px rgba(0, 0, 0, 0.4));
6177
}
6278

6379
.github-stars-badge {
6480
display: flex;
6581
align-items: center;
6682
transition: transform 0.2s ease;
67-
margin-left: 0.5rem;
83+
opacity: 0.9;
6884
}
6985

7086
.github-stars-badge:hover {
@@ -87,10 +103,10 @@ header {
87103
}
88104

89105
.brand-divider {
90-
height: 18px;
106+
height: 40px; /* Taller divider for the larger logo */
91107
width: 1px;
92108
background: var(--border);
93-
margin: 0 0.75rem 0 0;
109+
margin: 0 1.5rem; /* Symmetric horizontal spacing */
94110
}
95111

96112
.logo-link {
@@ -99,20 +115,21 @@ header {
99115
display: inline-block;
100116
}
101117

102-
h1 {
103-
font-size: 1.75rem;
104-
font-weight: 800;
105-
line-height: 1.1;
106-
background: linear-gradient(to right, #818cf8, #c084fc);
107-
-webkit-background-clip: text;
108-
background-clip: text;
109-
-webkit-text-fill-color: transparent;
110-
letter-spacing: -0.025em;
111-
transition: all 0.3s ease;
118+
.logo-link:hover .main-logo {
119+
transform: scale(1.02) translateY(-4px);
120+
filter: drop-shadow(0 12px 24px rgba(99, 102, 241, 0.5)) brightness(1.1);
112121
}
113122

114-
.logo-link:hover h1 {
115-
filter: brightness(1.2);
123+
.sr-only {
124+
position: absolute;
125+
width: 1px;
126+
height: 1px;
127+
padding: 0;
128+
margin: -1px;
129+
overflow: hidden;
130+
clip: rect(0, 0, 0, 0);
131+
white-space: nowrap;
132+
border-width: 0;
116133
}
117134

118135
.subtitle {

templates/index.html

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,37 @@
44
<head>
55
<meta charset="UTF-8">
66
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7-
<title>InfraScan - Advanced Infrastructure Auditor</title>
7+
<meta name="description"
8+
content="InfraScan is an open-source Infrastructure as Code scanner that identifies security vulnerabilities, cost optimization opportunities, and compliance issues in Terraform, CloudFormation, Kubernetes, and Dockerfile. Scan GitHub repositories for free.">
9+
<meta name="keywords"
10+
content="IaC scanner, Terraform security, CloudFormation audit, Kubernetes scanning, infrastructure audit, security scanner, cost optimization">
11+
<meta name="author" content="SolDevelo">
12+
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1">
13+
14+
<!-- Open Graph / Facebook -->
15+
<meta property="og:type" content="website">
16+
<meta property="og:url" content="{{ site_domain }}/">
17+
<meta property="og:title" content="InfraScan - Free Infrastructure as Code Security Scanner">
18+
<meta property="og:description"
19+
content="Scan Infrastructure as Code for security vulnerabilities and cost optimization. Supports Terraform, CloudFormation, Kubernetes, and more.">
20+
<meta property="og:image" content="{{ site_domain }}{{ url_for('static', filename='images/logo_transparent.png') }}">
21+
22+
<!-- Twitter -->
23+
<meta property="twitter:card" content="summary">
24+
<meta property="twitter:url" content="{{ site_domain }}/">
25+
<meta property="twitter:title" content="InfraScan - Free Infrastructure as Code Security Scanner">
26+
<meta property="twitter:description"
27+
content="Scan Infrastructure as Code for security vulnerabilities and cost optimization. Supports Terraform, CloudFormation, Kubernetes, and more.">
28+
<meta property="twitter:image" content="{{ site_domain }}{{ url_for('static', filename='images/logo_transparent.png') }}">
29+
30+
<!-- Canonical -->
31+
<link rel="canonical" href="{{ site_domain }}/">
32+
33+
<!-- Language -->
34+
<meta http-equiv="content-language" content="en-US">
35+
36+
<title>InfraScan - Free Infrastructure as Code Security Scanner | Terraform & CloudFormation Auditor</title>
37+
838
{% if google_tag_id %}
939
<!-- Google tag (gtag.js) -->
1040
<script async src="https://www.googletagmanager.com/gtag/js?id={{ google_tag_id }}"></script>
@@ -16,30 +46,61 @@
1646
gtag('config', '{{ google_tag_id }}');
1747
</script>
1848
{% endif %}
49+
50+
<!-- JSON-LD Structured Data -->
51+
<script type="application/ld+json">
52+
{
53+
"@context": "https://schema.org",
54+
"@type": "SoftwareApplication",
55+
"name": "InfraScan",
56+
"description": "Open-source Infrastructure as Code scanner for security and cost optimization",
57+
"url": "{{ site_domain }}",
58+
"applicationCategory": "DeveloperApplication",
59+
"offers": {
60+
"@type": "Offer",
61+
"price": "0",
62+
"priceCurrency": "USD"
63+
},
64+
"creator": {
65+
"@type": "Organization",
66+
"name": "SolDevelo",
67+
"url": "https://soldevelo.com"
68+
},
69+
"operatingSystem": "Web-based",
70+
"featureList": ["Terraform scanning", "CloudFormation audit", "Kubernetes manifest analysis", "Dockerfile security scan", "Cost optimization detection", "Security vulnerability detection"]
71+
}
72+
</script>
73+
1974
<link rel="preconnect" href="https://fonts.googleapis.com">
2075
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
2176
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
77+
<!-- Preload critical resources -->
78+
<link rel="preload" as="style" href="{{ url_for('static', filename='style.css') }}?v={{ static_version }}">
2279
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}?v={{ static_version }}">
2380
</head>
2481

2582
<body>
2683
<div class="container">
2784
<header>
2885
<div class="logo">
29-
<a href="{{ url_for('index') }}" class="logo-link">
30-
<h1>InfraScan</h1>
31-
</a>
86+
<div class="main-brand-row">
87+
<a href="{{ url_for('index') }}" class="logo-link">
88+
<img src="{{ url_for('static', filename='images/logo_transparent.png') }}" alt="InfraScan"
89+
class="main-logo" loading="eager" decoding="async">
90+
<h1 class="sr-only">InfraScan</h1>
91+
</a>
92+
<div class="soldevelo-brand">
93+
<span class="brand-divider"></span>
94+
<a href="https://soldevelo.com?utm_source=infrascan&utm_medium=referral&utm_campaign=app_transition&utm_content=header-logo"
95+
target="_blank">
96+
<img src="{{ url_for('static', filename='images/soldevelo.png') }}" alt="SolDevelo"
97+
class="header-logo">
98+
</a>
99+
</div>
100+
</div>
32101
<a href="https://github.com/SolDevelo/InfraScan" target="_blank" class="github-stars-badge">
33102
<img src="https://img.shields.io/github/stars/SolDevelo/InfraScan?style=social" alt="GitHub stars">
34103
</a>
35-
<div class="soldevelo-brand">
36-
<span class="brand-divider"></span>
37-
<a href="https://soldevelo.com?utm_source=infrascan&utm_medium=referral&utm_campaign=app_transition&utm_content=header-logo"
38-
target="_blank">
39-
<img src="{{ url_for('static', filename='images/soldevelo.png') }}" alt="SolDevelo"
40-
class="header-logo">
41-
</a>
42-
</div>
43104
</div>
44105
<p class="subtitle">Open Source Infrastructure Auditor by <strong>SolDevelo</strong></p>
45106

0 commit comments

Comments
 (0)