If you're building a SaaS product that serves EU customers — particularly in Germany and Austria — you need to take the GDPR seriously. The GDPR (General Data Protection Regulation, known as DSGVO in German: Datenschutz-Grundverordnung) applies to any company processing personal data of people in the EU, regardless of where your company is based.
I've audited dozens of SaaS products over the past year, and the same compliance gaps show up again and again. This guide covers the five critical areas you need to get right, with code examples showing the mistakes I see most often.
Why GDPR Matters for Developers
Let's be blunt: fines under the GDPR can reach up to 20 million euros or 4% of annual global turnover, whichever is higher. But beyond fines, EU users — especially German ones — are privacy-conscious. A missing legal notice or a broken cookie banner will tank your credibility faster than a 500 error on your landing page.
In Germany, there's an additional risk: Abmahnung — a formal legal warning letter, often sent by competitors or privacy advocacy groups. These are essentially cease-and-desist notices that come with a bill, typically ranging from a few hundred to several thousand euros. They're common, and they target low-hanging compliance failures like a missing Impressum or an invalid cookie banner.
The good news: if you handle these five areas correctly, you're covering about 90% of what a typical SaaS needs.
1. Impressum (Legal Notice)
If you're serving customers in Germany or Austria, you need an Impressum — a legally mandated identification page. This isn't optional — it's required under Section 5 of the German TMG (Telemediengesetz, the Telemedia Act). There's no exact equivalent in US or UK law; think of it as a mandatory "About Us" page with legally prescribed content.
What Must Be Included
- Full legal name (company or individual)
- Physical postal address (no P.O. boxes)
- Contact email and phone number
- Commercial register number (if applicable)
- VAT identification number (Umsatzsteuer-ID)
- Responsible person for editorial content (if applicable)
Common Mistake: Hiding It in the Footer
Your Impressum must be reachable within two clicks from any page. A common anti-pattern:
// BAD: Impressum buried in a collapsed accordion
<footer>
<Accordion>
<AccordionItem title="Legal Stuff">
<Link href="/impressum">Impressum</Link>
</AccordionItem>
</Accordion>
</footer>
Instead, place it as a direct link in your footer:
// GOOD: Direct, visible link
<footer className="flex gap-4 text-sm">
<Link href="/impressum">Impressum</Link>
<Link href="/privacy">Privacy Policy</Link>
</footer>
Pro Tip
If you're a solo developer outside Germany, you still need an Impressum if you target German users. You can use a business address service (Geschäftsadresse) to avoid publishing your home address. Just make sure the address accepts physical mail.
2. Datenschutzerklärung (Privacy Policy)
A Datenschutzerklärung (literally: "data protection declaration") is the GDPR-compliant privacy policy. It goes well beyond the vague privacy policies common on US websites. German and Austrian courts have specific expectations about structure and content.
Required Sections
Your privacy policy must cover:
- Identity of the data controller (name, address, contact)
- Data Protection Officer contact (required if you process data as a core activity or have 20+ employees regularly processing personal data)
- Legal basis for each processing activity (Art. 6 GDPR)
- Categories of data collected
- Purpose of processing
- Data retention periods
- Third-party data processors (with specific names — not just "analytics providers")
- Data subject rights (access, rectification, erasure, portability, objection)
- Right to lodge a complaint with the supervisory authority
Common Mistake: Generic Legal Basis
<!-- BAD: Vague legal basis -->
We process your data based on legitimate interest.
You need to specify the legal basis per processing activity:
<!-- GOOD: Specific legal basis per activity -->
### Analytics (Plausible Analytics)
- Data collected: Page views, referrer, country
- Legal basis: Art. 6(1)(f) GDPR (legitimate interest)
- Legitimate interest: Understanding usage patterns to improve the service
- Retention period: 24 months
- Data processor: Plausible Insights OÜ, Estonia
### Account Registration
- Data collected: Email address, name
- Legal basis: Art. 6(1)(b) GDPR (contract performance)
- Retention period: Duration of account + 30 days
Third-Party Services
Every external service must be named explicitly. This includes:
- Analytics (Google Analytics, Plausible, Fathom)
- Payment processors (Stripe, Lemon Squeezy, PayPal)
- Email services (Resend, SendGrid, Mailchimp)
- CDNs (Cloudflare, Vercel, AWS CloudFront)
- Error tracking (Sentry, LogRocket)
If your service transfers data outside the EU (e.g., to US-based providers), you must document the legal mechanism for that transfer — typically Standard Contractual Clauses (SCCs) or an EU adequacy decision.
3. Cookie Consent
This is where most SaaS products fail. The GDPR, combined with the ePrivacy Directive (implemented in Germany as the TTDSG — Telekommunikation-Telemedien-Datenschutz-Gesetz), requires prior consent for any non-essential cookies or tracking.
What Requires Consent
- Analytics cookies (even "privacy-friendly" ones, if they set cookies)
- Marketing/advertising trackers
- Social media embeds
- A/B testing tools that use cookies
What Does NOT Require Consent
- Session cookies for authentication
- Shopping cart cookies
- Language preference cookies
- Cookies technically necessary for the service
Common Mistake: Pre-checked Consent
// BAD: Pre-checked analytics checkbox
<label>
<input type="checkbox" defaultChecked={true} />
Analytics cookies
</label>
This violates the GDPR. Consent must be freely given, meaning no pre-checked boxes:
// GOOD: Unchecked by default, clear labeling
const [analyticsConsent, setAnalyticsConsent] = useState(false);
<label>
<input
type="checkbox"
checked={analyticsConsent}
onChange={(e) => setAnalyticsConsent(e.target.checked)}
/>
Analytics cookies — Help us understand how you use the site
<Link href="/privacy#analytics">Learn more</Link>
</label>
The "Reject All" Button
EU regulators (and German courts in particular) have ruled that rejecting cookies must be as easy as accepting them. That means your "Reject All" button must be as prominent as "Accept All":
// GOOD: Equal prominence for both options
<div className="flex gap-3">
<Button variant="outline" onClick={rejectAll}>
Reject All
</Button>
<Button variant="outline" onClick={acceptAll}>
Accept All
</Button>
</div>
Note both buttons use the same variant. Making "Accept" a primary button and "Reject" a subtle text link is a dark pattern that regulators have specifically called out — in Germany, this can trigger an Abmahnung.
Loading Scripts Conditionally
Never load tracking scripts before consent is given:
// BAD: Loading analytics unconditionally
<Script src="https://analytics.example.com/script.js" />
// GOOD: Only load after consent
{hasAnalyticsConsent && (
<Script src="https://analytics.example.com/script.js" />
)}
4. DSAR Handling (Data Subject Access Requests)
Under Art. 15 GDPR, any user can request a copy of all personal data you hold about them. You must respond within one month. This right applies to all EU residents, not just German users.
What You Need
- A way to receive DSARs (email address or form)
- Identity verification before sharing data
- A process to export all user data
- The ability to delete user data on request (Art. 17 — the "Right to Erasure")
Common Mistake: No Data Export
If your app stores user data across multiple tables or services, you need a way to aggregate it:
// API route: /api/dsar/export
export async function GET(request: Request) {
const user = await authenticateRequest(request);
const [profile, orders, activityLog, supportTickets] =
await Promise.all([
db.users.findUnique({ where: { id: user.id } }),
db.orders.findMany({ where: { userId: user.id } }),
db.activityLog.findMany({ where: { userId: user.id } }),
db.supportTickets.findMany({ where: { userId: user.id } }),
]);
return Response.json({
exportDate: new Date().toISOString(),
profile,
orders,
activityLog,
supportTickets,
});
}
Account Deletion
Don't just soft-delete. If a user requests erasure under Art. 17, you need to actually remove their personal data (with some exceptions for legal retention requirements — for example, German tax law requires you to keep invoices for 10 years):
async function handleErasureRequest(userId: string) {
// Delete personal data
await db.users.update({
where: { id: userId },
data: {
email: \`deleted-\${userId}@redacted.local\`,
name: "Deleted User",
phone: null,
address: null,
},
});
// Keep anonymized order records (tax retention: 10 years in Germany)
await db.orders.updateMany({
where: { userId },
data: { customerName: "Deleted User", customerEmail: null },
});
// Purge activity logs
await db.activityLog.deleteMany({ where: { userId } });
}
5. DPA — Data Processing Agreement (AV-Vertrag)
If you use any third-party service that processes personal data on your behalf, the GDPR (Art. 28) requires a signed Data Processing Agreement (DPA). In German, this is called an Auftragsverarbeitungsvertrag or AV-Vertrag — you'll see this term frequently when dealing with German-based providers.
Services That Require a DPA
- Cloud hosting (Vercel, AWS, Hetzner)
- Email delivery (Resend, SendGrid)
- Payment processing (Stripe, Lemon Squeezy)
- Analytics (if data is processed by them)
- Customer support tools (Intercom, Zendesk)
- Error tracking (Sentry)
How to Get One
Most large providers offer a DPA you can sign online:
- Vercel: Settings > Legal > DPA
- Stripe: Available in the Stripe Dashboard
- AWS: Part of the AWS Service Terms
- Hetzner: Available in the Hetzner account area
Store these signed agreements. If a supervisory authority audits you, they will ask for them.
Common Mistake: Forgetting Subprocessors
Your DPA should list subprocessors. If Vercel uses AWS, and you have a DPA with Vercel, Vercel's agreement should cover their use of AWS. Check the subprocessor lists of your providers regularly.
The Complete Checklist
Here's your actionable checklist. Go through each item:
- [ ] Impressum visible and reachable within 2 clicks from every page
- [ ] Impressum contains full name, address, email, phone, register number
- [ ] Privacy Policy (Datenschutzerklärung) covers all data processing activities
- [ ] Each processing activity lists its legal basis (Art. 6 GDPR)
- [ ] All third-party services named with their purpose and location
- [ ] Data transfer mechanisms documented for non-EU processors (SCCs, adequacy decisions)
- [ ] Data retention periods specified per category
- [ ] Cookie consent banner with equal-weight Accept/Reject buttons
- [ ] No tracking scripts loaded before consent
- [ ] Cookie preferences revocable at any time
- [ ] DSAR process documented and reachable
- [ ] Data export endpoint implemented
- [ ] Account deletion actually removes personal data
- [ ] Response process meets 30-day deadline
- [ ] DPAs signed with all data processors
- [ ] Subprocessor lists reviewed
- [ ] Agreements stored and accessible
Skip the Research, Ship Compliant
Putting all of this together from scratch takes days of research and legal review. If you want to skip ahead, I built the GDPR Starter Kit for Next.js — a drop-in compliance package for Next.js SaaS projects targeting EU customers.
It includes:
- Pre-built Impressum and privacy policy templates (English/German)
- Cookie consent component with GDPR/TTDSG-compliant behavior
- DSAR handling API routes with data export and deletion
- DPA checklist and template
- All the code snippets from this article and more
Get the GDPR Starter Kit for Next.js — €79 →
Have questions about GDPR compliance for your SaaS? Drop them in the comments — I'll do my best to help.
Top comments (0)