Ahrefs 96->100

This commit is contained in:
Timo Knuth 2026-01-15 10:14:40 +01:00
parent 0a02876ea4
commit 83ea141230
17 changed files with 694 additions and 579 deletions

View File

@ -0,0 +1 @@
bb6dfaacf1ed41a880281c426c54ed7c

View File

@ -4,7 +4,7 @@ import fs from 'fs';
import path from 'path';
const HOST = 'www.qrmaster.net';
const KEY = '1234567890abcdef';
const KEY = 'bb6dfaacf1ed41a880281c426c54ed7c';
const KEY_LOCATION = `https://${HOST}/${KEY}.txt`;
const INDEXNOW_ENDPOINT = 'https://api.indexnow.org/indexnow';

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,14 @@ import AppLayout from './AppLayout';
export const metadata: Metadata = {
title: 'Dashboard | QR Master',
description: 'Manage your QR Master dashboard. Create dynamic QR codes, view real-time scan analytics, and configure your account settings in one secure place.',
robots: { index: false, follow: false }, // Dashboard pages shouldn't be indexed generally
robots: { index: false, follow: false },
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/logo.svg', type: 'image/svg+xml' },
],
apple: '/logo.svg',
},
};
export default function RootAppLayout({

View File

@ -5,7 +5,13 @@ import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Authentication | QR Master',
description: 'Securely login or sign up to QR Master to manage your dynamic QR codes, track analytics, and access premium features. Your gateway to professional QR management.',
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/logo.svg', type: 'image/svg+xml' },
],
apple: '/logo.svg',
},
};
export default function AuthRootLayout({

View File

@ -11,6 +11,24 @@ export const metadata: Metadata = {
alternates: {
canonical: 'https://www.qrmaster.net/login',
},
openGraph: {
title: 'Login to QR Master | Access Your Dashboard',
description: 'Sign in to QR Master to create, manage, and track your QR codes.',
url: 'https://www.qrmaster.net/login',
type: 'website',
images: [{
url: 'https://www.qrmaster.net/og-image.png',
width: 1200,
height: 630,
alt: 'QR Master Login',
}],
},
twitter: {
card: 'summary_large_image',
title: 'Login to QR Master | Access Your Dashboard',
description: 'Sign in to QR Master to create, manage, and track your QR codes.',
images: ['https://www.qrmaster.net/og-image.png'],
},
};

View File

@ -11,6 +11,24 @@ export const metadata: Metadata = {
alternates: {
canonical: 'https://www.qrmaster.net/signup',
},
openGraph: {
title: 'Create Free Account | QR Master',
description: 'Sign up for QR Master to create free QR codes with tracking and customization.',
url: 'https://www.qrmaster.net/signup',
type: 'website',
images: [{
url: 'https://www.qrmaster.net/og-image.png',
width: 1200,
height: 630,
alt: 'QR Master Sign Up',
}],
},
twitter: {
card: 'summary_large_image',
title: 'Create Free Account | QR Master',
description: 'Sign up for QR Master to create free QR codes with tracking and customization.',
images: ['https://www.qrmaster.net/og-image.png'],
},
};

View File

@ -0,0 +1,22 @@
'use client';
import React from 'react';
import { ObfuscatedMailto } from '@/components/ui/ObfuscatedMailto';
export function ContactSupport() {
return (
<div className="mt-16 bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-lg">
<h2 className="text-2xl font-bold mb-4 text-gray-900">
Still have questions?
</h2>
<p className="text-lg text-gray-700 mb-6 leading-relaxed">
Our support team is here to help. Contact us at{' '}
<ObfuscatedMailto
email="support@qrmaster.net"
className="text-blue-600 hover:text-blue-700 font-semibold"
/>{' '}
or reach out through our live chat.
</p>
</div>
);
}

View File

@ -3,6 +3,7 @@ import type { Metadata } from 'next';
import SeoJsonLd from '@/components/SeoJsonLd';
import { faqPageSchema } from '@/lib/schema';
import { Card, CardContent } from '@/components/ui/Card';
import { ContactSupport } from './ContactSupport';
function truncateAtWord(text: string, maxLength: number): string {
if (text.length <= maxLength) return text;
@ -131,18 +132,7 @@ export default function FAQPage() {
))}
</div>
<div className="mt-16 bg-blue-50 border-l-4 border-blue-500 p-8 rounded-r-lg">
<h2 className="text-2xl font-bold mb-4 text-gray-900">
Still have questions?
</h2>
<p className="text-lg text-gray-700 mb-6 leading-relaxed">
Our support team is here to help. Contact us at{' '}
<a href="mailto:support@qrmaster.net" className="text-blue-600 hover:text-blue-700 font-semibold">
support@qrmaster.net
</a>{' '}
or reach out through our live chat.
</p>
</div>
<ContactSupport />
</div>
</div>
</div>

View File

@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/Badge';
import { showToast } from '@/components/ui/Toast';
import { useRouter } from 'next/navigation';
import { BillingToggle } from '@/components/ui/BillingToggle';
import { ObfuscatedMailto } from '@/components/ui/ObfuscatedMailto';
export default function PricingClient() {
const router = useRouter();
@ -182,9 +183,9 @@ export default function PricingClient() {
return (
<div className="container mx-auto px-4 py-12">
<div className="text-center mb-12">
<h1 className="text-4xl font-bold text-gray-900 mb-4">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Choose Your Plan
</h1>
</h2>
<p className="text-xl text-gray-600">
Select the perfect plan for your QR code needs
</p>
@ -260,7 +261,7 @@ export default function PricingClient() {
All plans include unlimited static QR codes and basic customization.
</p>
<p className="text-gray-600 mt-2">
Need help choosing? <a href="mailto:support@qrmaster.net" className="text-primary-600 hover:text-primary-700 underline">Contact our team</a>
Need help choosing? <ObfuscatedMailto email="support@qrmaster.net" className="text-primary-600 hover:text-primary-700 underline">Contact our team</ObfuscatedMailto>
</p>
</div>
</div>

View File

@ -0,0 +1,13 @@
'use client';
import React from 'react';
import { ObfuscatedMailto } from '@/components/ui/ObfuscatedMailto';
export function PrivacyEmailLink() {
return (
<ObfuscatedMailto
email="support@qrmaster.net"
className="text-primary-600 hover:text-primary-700"
/>
);
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import Link from 'next/link';
import { PrivacyEmailLink } from './PrivacyEmailLink';
export const metadata = {
title: 'Privacy Policy | QR Master',
@ -110,9 +111,7 @@ export default function PrivacyPage() {
</ul>
<p className="text-gray-700 mb-4">
To exercise these rights, contact us at{' '}
<a href="mailto:support@qrmaster.net" className="text-primary-600 hover:text-primary-700">
support@qrmaster.net
</a>
<PrivacyEmailLink />
</p>
<p className="text-gray-700 mb-4">
Our service is for users 16 years and older. If you're in the EEA and have concerns,
@ -128,9 +127,7 @@ export default function PrivacyPage() {
<div className="bg-gray-50 p-6 rounded-lg">
<p className="text-gray-700 mb-2">
<strong>Email:</strong>{' '}
<a href="mailto:support@qrmaster.net" className="text-primary-600 hover:text-primary-700">
support@qrmaster.net
</a>
<PrivacyEmailLink />
</p>
<p className="text-gray-700 mb-2"><strong>Website:</strong> <a href="/" className="text-primary-600 hover:text-primary-700">qrmaster.net</a></p>
</div>

View File

@ -4,6 +4,13 @@ import '@/styles/globals.css';
export const metadata = {
title: 'vCard Download',
description: 'Download contact information',
icons: {
icon: [
{ url: '/favicon.svg', type: 'image/svg+xml' },
{ url: '/logo.svg', type: 'image/svg+xml' },
],
apple: '/logo.svg',
},
};
export default function VCardLayout({

View File

@ -10,10 +10,10 @@ export default function SeoJsonLd({ data }: SeoJsonLdProps) {
return (
<>
{jsonLdArray.map((item, index) => {
const schema = {
'@context': 'https://schema.org',
...item,
};
// Only add @context if it doesn't already exist in the item
const schema = (item as any)['@context']
? item
: { '@context': 'https://schema.org', ...item };
return (
<script

View File

@ -66,9 +66,9 @@ export const Hero: React.FC<HeroProps> = ({ t }) => {
transition={{ duration: 0.5 }}
className="space-y-6"
>
<h1 className="text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
<h2 className="text-5xl lg:text-6xl font-bold text-gray-900 leading-tight">
{t.hero.title}
</h1>
</h2>
<p className="text-xl text-gray-600 leading-relaxed max-w-2xl">
{t.hero.subtitle}

View File

@ -0,0 +1,34 @@
'use client';
import React, { useState, useEffect } from 'react';
interface ObfuscatedMailtoProps {
email: string;
className?: string;
children?: React.ReactNode;
}
/**
* Renders an email link that only becomes a clickable mailto: link after client-side hydration.
* This prevents Cloudflare's Email Obfuscation from breaking the link in the static HTML,
* which causes crawlers like Ahrefs to report 404 errors on /cdn-cgi/l/email-protection.
*/
export function ObfuscatedMailto({ email, className, children }: ObfuscatedMailtoProps) {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
// Before hydration, render as plain text/span to avoid Cloudflare manipulation
if (!isMounted) {
return <span className={className}>{children || email}</span>;
}
// After hydration, render as a proper mailto link
return (
<a href={`mailto:${email}`} className={className}>
{children || email}
</a>
);
}

View File

@ -21,7 +21,7 @@ export const blogPosts: Record<string, BlogPostData> = {
'qr-code-analytics': {
slug: 'qr-code-analytics',
title: 'QR Code Analytics: The Complete Guide',
excerpt: 'Master QR Code Analytics with our complete guide. Learn how to track scans, measure ROI, and optimize your marketing campaigns using real-time data and insights.',
excerpt: 'Master QR Code Analytics with our complete guide. Learn how to track scans, measure ROI, and optimize your marketing campaigns using real-time data.',
date: 'October 16, 2025',
datePublished: '2025-10-16T09:00:00Z',
dateModified: '2025-10-16T09:00:00Z',
@ -142,7 +142,7 @@ export const blogPosts: Record<string, BlogPostData> = {
'qr-code-tracking-guide-2025': {
slug: 'qr-code-tracking-guide-2025',
title: 'QR Code Tracking: Complete Guide 2025',
excerpt: 'The complete guide to QR Code Tracking in 2025. Learn how to track scans, measure ROI with analytics tools, and optimize your marketing campaigns for maximum engagement.',
excerpt: 'The complete guide to QR Code Tracking in 2025. Learn how to track scans, measure ROI, and optimize your marketing campaigns.',
date: 'October 18, 2025',
datePublished: '2025-10-18T09:00:00Z',
dateModified: '2025-10-18T09:00:00Z',
@ -668,7 +668,7 @@ app.get('/qr/:id', async (req, res) => {
'dynamic-vs-static-qr-codes': {
slug: 'dynamic-vs-static-qr-codes',
title: 'Dynamic vs Static QR Codes: The Ultimate Comparison',
excerpt: 'Static vs Dynamic QR Codes: Which one should you choose? Learn the key differences, pros and cons, and why dynamic QR codes are the better choice for business and marketing.',
excerpt: 'Static vs Dynamic QR Codes: Which should you choose? Learn the key differences, pros and cons, and why dynamic codes are better for business.',
date: 'October 17, 2025',
datePublished: '2025-10-17T09:00:00Z',
dateModified: '2025-10-17T09:00:00Z',