Back to Blog

Compliance

Danish Data Protection Laws: A Developer's Practical Guide

· · 13 min read

Practical implementation guide for Danish GDPR requirements in web and app development, with code examples, a case study on cookieless analytics, and a compliance checklist you can use on your next project.

Legal disclaimer: This article provides general technical guidance for developers. It is not legal advice. For specific compliance questions, consult a qualified Danish data protection lawyer. The information here reflects our understanding as of 2025 and may be affected by future regulatory changes or Datatilsynet guidance.

GDPR in Denmark: What Developers Actually Need to Know

Most GDPR guides for developers are written for a generic EU audience and focus on theoretical principles. This guide is different. It is written specifically for developers building web applications and mobile apps for Danish businesses or Danish users, and it focuses on practical implementation, what to actually build and how to build it.

Denmark enforces GDPR through the Danish Data Protection Act (Databeskyttelsesloven) and the Danish Data Protection Agency (Datatilsynet). While the core GDPR rules are the same across the EU, Denmark has specific interpretations and additional requirements that affect how you implement data protection in your applications.

Key Danish-Specific Considerations:

  • Datatilsynet is active: The Danish DPA is one of the more proactive EU data protection authorities. They publish specific guidance and actively investigate complaints
  • CPR number protection: Danish CPR numbers (civil registration numbers) are classified as sensitive personal data under the Danish Data Protection Act and have additional restrictions
  • Cookie/consent rules: The Danish Executive Order on Cookies (cookiebekendtgoerelsen) implements the ePrivacy Directive with specific consent requirements
  • Bookkeeping overlap: The Danish Bookkeeping Act (bogfoeringsloven) requires 5-year retention of financial records, which can conflict with GDPR minimization, you must navigate both

As a developer, you need to understand three overlapping legal frameworks that govern data protection in Danish web applications:

GDPR (EU Regulation 2016/679)

The core regulation. Directly applicable in Denmark without national transposition. Establishes principles of lawful processing, data minimization, purpose limitation, and data subject rights. Fines up to 4% of global annual turnover or 20 million EUR.

Danish Data Protection Act (Databeskyttelsesloven)

Supplements GDPR with Danish-specific rules. Key additions: special rules for CPR numbers (section 11), rules on processing for journalism and research (sections 8-10), and the legal basis for Datatilsynet as the supervisory authority. Also sets the age of digital consent at 13 years (GDPR allows member states to set this between 13-16).

Implemented in Denmark through the Executive Order on Information and Consent Required for Storing or Accessing Information in End-User Terminal Equipment (cookiebekendtgoerelsen). Requires informed consent before placing non-essential cookies or using similar tracking technologies. This is the law that mandates cookie consent banners.

For developers, the practical implication is: you need to think about data protection at three levels. What personal data are you collecting (GDPR)? Are there any Danish-specific rules for that data type (Databeskyttelsesloven)? Are you using cookies or device fingerprinting to track users (ePrivacy/Cookie law)?

Lawful Basis for Processing: A Practical Guide

Every piece of personal data you process needs a lawful basis. This is not bureaucracy, it is the foundation of your data architecture. The lawful basis determines what consent flows you need, how long you can keep data, and what rights users have over that data. Choose the wrong basis and your entire data pipeline may be non-compliant.

User explicitly agrees to data processing

  • When to use: Marketing emails, analytics cookies, third-party tracking
  • Developer action: Implement consent banner, store consent records, respect withdrawal

Contract

Processing necessary to fulfill a contract

  • When to use: Delivering a purchased service, processing payments, account management
  • Developer action: No consent banner needed, but inform users in privacy policy

Legitimate Interest

Processing necessary for legitimate business purposes

  • When to use: Fraud prevention, security logging, basic analytics without tracking
  • Developer action: Document the legitimate interest assessment, provide opt-out

Processing required by law

  • When to use: Tax records, bookkeeping data (Danish bogfoeringsloven), AML checks
  • Developer action: Retain required data for mandated periods, even if user requests deletion

Common Developer Mistake

Many developers default to “consent” as the lawful basis for everything. This is incorrect and actually creates more problems. If you use consent as your basis, users can withdraw it at any time, and you must immediately stop processing and potentially delete all data. For core service functionality (account management, payment processing, service delivery), “contract” is usually the correct basis. Reserve “consent” for optional features like marketing and third-party analytics.

When consent is the appropriate lawful basis, the GDPR (and Datatilsynet’s guidance) sets a high bar for what counts as valid consent. It must be freely given, specific, informed, and unambiguous. Here is what that means in practice:

Do not use a single “accept all” button. Datatilsynet expects users to be able to consent to different categories independently. At minimum, separate: strictly necessary (no consent needed), functional, analytics, and marketing cookies.

// Cookie consent state structure
interface CookieConsent {
  necessary: true       // Always true, no toggle
  functional: boolean   // Language preferences, UI settings
  analytics: boolean    // Usage statistics, heatmaps
  marketing: boolean    // Ad targeting, social media pixels
  consentDate: string   // ISO date of consent
  consentVersion: string // Version of privacy policy
}

// Store consent server-side (not just in a cookie)
async function saveConsent(consent: CookieConsent) {
  await db.consentRecord.create({
    data: {
      ...consent,
      ipHash: hashIP(request.ip), // Hash, don't store raw
      userAgent: request.headers['user-agent'],
      timestamp: new Date().toISOString()
    }
  })
}

GDPR requires you to prove that consent was obtained (accountability principle). Store consent records server-side with enough detail to demonstrate: who consented, when, to what, and what information they were shown. Keep these records for at least 2 years after the consent expires or is withdrawn.

Datatilsynet can request proof of consent during an investigation

Anti-Pattern: Dark Patterns

Datatilsynet and the European Data Protection Board have explicitly warned against dark patterns in consent flows. Avoid:

  • Making “Accept All” visually prominent while hiding “Reject All” or “Manage Settings”
  • Pre-checking consent checkboxes (consent must require affirmative action)
  • Requiring more clicks to reject than to accept
  • Cookie walls that block content until consent is given (unless genuinely necessary)

At TwinCurrent we built our own privacy-first analytics system (tw.js). It sets no cookies and builds no cross-site profile, but it is not storage-free: it keeps a rotating session id in sessionStorage and a short retry queue in localStorage so events are not lost on a flaky connection. That distinction matters legally, and it is worth walking through honestly rather than claiming more than the code does.

The ePrivacy Directive (and Denmark’s cookie law) requires consent for “storing or accessing information in end-user terminal equipment.” If your analytics system does not store cookies, local storage data, or any other information on the user’s device, the ePrivacy consent requirement does not apply. The remaining question is GDPR compliance for the server-side data processing, which can be handled under legitimate interest.

How tw.js Works

Our tracker derives a session id from coarse browser characteristics rather than a cookie. The id itself is held in sessionStorage so a single visit is not counted many times, which is a device write and is why we treat it as consent-gated rather than exempt:

// Simplified fingerprint generation (client-side)
// No cookies. The derived id is kept in sessionStorage for the visit,
// and failed events queue in localStorage until they can be retried.
function generateSessionId() {
  const components = [
    navigator.language,
    screen.width + 'x' + screen.height,
    new Date().toDateString(), // Rotates daily
    // Additional entropy from browser features
  ]

  // Hash the components - one-way, not reversible
  return hashComponents(components)
}

// Server-side: the fingerprint hash is stored,
// never the raw components
// IP addresses are NOT stored - only used for
// geolocation lookup, then discarded

Privacy by Design Principles Applied

  • Minimal device storage, no cookies No cookies and no persistent identifier. Two keys are written: a rotating session id in sessionStorage that is gone when the tab closes, and a short-lived retry queue in localStorage for events that failed to send. Both are device writes under ePrivacy, so they belong behind consent, not in an exemption
  • No cross-site tracking Session IDs rotate daily and are site-specific. A user cannot be tracked across different websites
  • IP address handling IP addresses are used only for geographic lookup (country/region level), then immediately discarded. Never stored in the database
  • Data minimization Only aggregate metrics are stored: page views, session counts, referrers, browser types. No individual user profiles are created
  • Self-hosted All data stays on our own servers. No third-party data processors involved in the analytics pipeline, eliminating the need for Data Processing Agreements for analytics

Lawful Basis: Legitimate Interest

For the server-side processing of anonymous analytics data, we rely on legitimate interest (GDPR Article 6(1)(f)). The legitimate interest assessment:

  • Purpose: Understanding website usage to improve the service (page performance, popular content, error tracking)
  • Necessity: Basic analytics are necessary for operating a website effectively. Cannot be achieved with less data
  • Balance: Minimal impact on users (no device storage, no tracking, no profiling). User interests are not overridden

This approach is used across all TwinCurrent properties and client sites. It provides the analytics data we need (page views, sessions, referrers, Core Web Vitals, error tracking) without requiring a consent banner for analytics. The result: a cleaner user experience and full GDPR compliance.

Data Storage and Retention Requirements

GDPR’s data minimization principle says you should not keep personal data longer than necessary. But Danish law also mandates minimum retention periods for certain data types, particularly financial records. Navigating these overlapping requirements is one of the trickiest parts of Danish data protection compliance.

Data Type Retention Period Legal Basis Notes
Financial records / bookkeeping 5 years Danish Bookkeeping Act (bogfoeringsloven) Mandatory retention - cannot delete even if user requests it
Tax-related documents 5 years Danish Tax Assessment Act Includes invoices, expense receipts, VAT records
Employment records 5 years after employment ends Various Danish employment laws Salary records, contracts, tax withholding
User account data Duration of service + 30 days Contract / Legitimate interest Delete after account closure grace period
Server logs (with IP) 90 days maximum Legitimate interest (security) Anonymize or delete after security review window
Marketing consent records Duration of consent + 2 years Accountability principle Keep records to prove consent was given
Analytics data (with PII) 26 months Legitimate interest / Consent Anonymize after retention period

Implementation: Automated Data Lifecycle

Do not rely on manual deletion. Implement automated data lifecycle management:

// Example: Automated data retention enforcement
// Run as a daily cron job

async function enforceDataRetention() {
  const now = new Date()

  // Delete server logs older than 90 days
  await db.serverLog.deleteMany({
    where: {
      createdAt: { lt: subDays(now, 90) }
    }
  })

  // Anonymize analytics data older than 26 months
  await db.analyticsEvent.updateMany({
    where: {
      createdAt: { lt: subMonths(now, 26) },
      anonymized: false
    },
    data: {
      sessionId: 'anonymized',
      fingerprint: 'anonymized',
      anonymized: true
    }
  })

  // Delete inactive user accounts after 30-day grace
  await db.user.deleteMany({
    where: {
      deletionRequestedAt: { lt: subDays(now, 30) },
      status: 'pending_deletion'
    }
  })

  // NOTE: Financial records are NEVER auto-deleted
  // They must be retained for 5 years per Danish law
}

The Bookkeeping Conflict

When a user exercises their right to deletion (GDPR Article 17), you must comply, except for data you are legally required to retain. Financial records tied to the user (invoices, payment records, expense data) must be kept for 5 years under the Danish Bookkeeping Act. The solution: delete the user’s account and personal data, but retain the financial records in anonymized form (replace the name with a reference number, keep the amounts and dates).

Implementing Data Subject Rights

GDPR gives individuals specific rights over their data. As a developer, you need to build technical mechanisms that enable these rights. Datatilsynet expects you to respond to requests within 30 days. Here are the key rights and how to implement them:

Right of Access (Article 15)

Users can request a copy of all personal data you hold about them. Build an export function that compiles all user data into a readable format (JSON or PDF).

Implementation: Data export endpoint that queries all tables containing user PII

Right to Erasure (Article 17)

Users can request deletion of their data. Must be honored unless legal retention requirements apply. Implement as a soft delete (mark for deletion) with a grace period, then hard delete after 30 days. Retain legally required records in anonymized form.

Implementation: Soft delete + 30-day grace + automated hard delete cron

Right to Data Portability (Article 20)

Users can request their data in a machine-readable format (JSON, CSV). This overlaps with the right of access but specifically requires a structured, commonly used, and machine-readable format.

Implementation: JSON/CSV export of user-provided data (not derived analytics)

Right to Rectification (Article 16)

Users can correct inaccurate data. In most web applications, this is handled by profile editing features. Ensure that corrections propagate to all systems where the data is replicated (search indexes, caches, backups).

Implementation: Profile edit + cache invalidation + backup update policy

Developer Compliance Checklist

Use this checklist when building or auditing a web application for the Danish market. It covers the most common compliance requirements we encounter in our work at TwinCurrent.

Data Collection and Storage

  • Document every type of personal data collected and the lawful basis for each
  • Define retention periods for each data type and implement automated deletion
  • Encrypt personal data at rest (database encryption) and in transit (TLS)
  • Hash or anonymize data where full personal data is not needed (analytics, logs)
  • If storing CPR numbers: implement additional access controls per Danish Data Protection Act section 11
  • Implement granular cookie consent with separate categories (necessary, functional, analytics, marketing)
  • Do not load any non-essential tracking scripts before consent is obtained
  • Provide an equally easy way to reject as to accept cookies
  • Store consent records server-side with timestamps and consent version
  • Check whether your analytics writes to the device at all - cookies, localStorage and sessionStorage all trigger the consent rule, so dropping cookies alone does not exempt you

User Rights Implementation

  • Build a data export function (JSON/CSV) for access and portability requests
  • Implement account deletion with appropriate grace periods
  • Ensure profile editing propagates corrections across all data stores
  • Establish a process to respond to data subject requests within 30 days

Documentation and Accountability

  • Maintain a Record of Processing Activities (ROPA), required if you have more than 250 employees or process sensitive data
  • Publish a clear, accessible privacy policy in Danish (or the user’s language)
  • If using third-party processors: sign Data Processing Agreements (DPA) per GDPR Article 28
  • Document legitimate interest assessments for any processing based on that lawful basis
  • Implement breach notification procedures (72 hours to Datatilsynet, without undue delay to affected individuals)

Data protection compliance is not a one-time implementation. It is an ongoing practice that needs to be maintained as your application evolves, new features are added, and regulations are updated. Build these checks into your development process: not as an afterthought, but as a first-class requirement alongside security, testing, and performance.

Building for the Danish market?

We build GDPR-compliant applications with privacy by design. Every project includes data protection from day one, not bolted on after.

Get an estimate within 24 hours Our services

Related articles

Have a project in mind?

Tell us what you want to build. We reply within 24 hours with an honest assessment - no sales pitch.

Get in touch