Security & compliance
How Vakanssi protects candidate data, keeps CV files private, controls who can see what, and supports GDPR and CCPA compliance, all on your own server.
On this page(9)
Hiring means handling people’s personal data: names, contact details, CVs, and notes about them. This page explains, in plain terms, how Vakanssi keeps that data safe and how it helps you meet privacy rules. Because Vakanssi is self-hosted, all of it happens on your own server, under your control, with no third-party service holding your candidates’ data.
Not legal advice
This page describes what the plugin does. It is not legal advice. Whether your hiring process complies with GDPR, CCPA, or local law depends on how you run it, so check with your own advisor.
Where candidate data lives
Two places, both on your server:
- Your database. Applications, candidates, notes, interviews, and email logs live in Vakanssi’s own tables (prefixed
wp_vakanssi_), not scattered through WordPress posts. See Backups & data. - A protected files folder. Uploaded CVs are stored as files, kept out of public reach (below).
Nothing is sent to an outside hiring service. There is no cloud account holding your candidates.
CV and file security
A CV is the most sensitive thing a candidate hands you, so Vakanssi treats uploaded files carefully at every step.
Where files are stored
- Since v0.37.10, Vakanssi stores CVs in a guarded
vakanssifolder inside your uploads directory (wordpress.org does not allow a plugin to write outside the WordPress install) and hardens it: it writes an Apache.htaccessdeny rule and anindex.phpguard, generates an Nginx deny snippet, and then verifies over HTTP that the files really cannot be fetched. - The
VAKANSSI_STORAGE_DIRconstant still lets you move storage outside the web root yourself, if you want that extra layer. - A confirmed exposure, the folder answering a direct request, is reported as critical in Site Health and flagged on the Status tab. A check that could not complete gets a lower-severity warning instead, since an unanswered check is not proof of a leak.
- A check cannot complete when the site cannot request its own URLs, when uploads are served from another host (such as a CDN), or, for a custom
VAKANSSI_STORAGE_DIRfolder only, when it answered “not found” rather than “forbidden”. - Since v0.37.0, an uploaded CV is moved into place by WordPress’s own upload handler (
wp_handle_upload()), pointed at the protected folder for that one call. Vakanssi then confirms the file landed inside that folder under the random name it chose, and deletes it and refuses the upload if another plugin’s upload filter moved it anywhere else. - Each stored file gets a randomised filename (not the applicant’s original name) and owner-only file permissions (
0600), and the folder itself is restricted to your site’s own user account (0700), so another account on a shared server cannot even list what CVs exist. - On a multisite network, each site gets its own subfolder (
site-<id>), so a Data Reset or uninstall purge on one site can never delete another site’s candidate CVs.
What is accepted
- Uploads are limited to PDF and Word documents (PDF, DOC, DOCX) by default, up to a size you set (5 MB by default).
- The file type is verified by its actual content (magic bytes), not just its extension: a PDF has to start with the real PDF header, a
.dochas to carry the real Word binary signature, and a.docxhas to be a genuine Office ZIP package, not a renamed archive. - A filename carrying invisible right-to-left override characters, which can make a file look like
cv.pdfwhile it displays or ends differently, is cleaned before the name is stored, shown, downloaded, or brought in by the importer.
How files are served
Uploaded CVs are never linked directly. Every download goes through a single guarded endpoint that:
- requires the person to be signed in and to hold the right capability,
- checks a WordPress nonce on the link, tied to that user and that file, so a link cannot be forged or used by another account,
- authorises the request against the specific application the file belongs to,
- is rate-limited per user to prevent bulk scraping,
- verifies the stored bytes still match the hash recorded at upload time, refusing to serve a file that has changed on disk, and
- sends the file with an
X-Content-Type-Options: nosniffheader so browsers cannot be tricked into running it.
A request that fails any of these gets a plain error page (403, 404, or 429, or a 400 from WordPress itself for someone not signed in), never the file.
Developers
The storage location and accepted types are filterable (vakanssi/storage/base_dir, vakanssi/storage/allowed_mimes), a virus scanner can veto an upload through vakanssi/storage/validate_file, and the download rate limit (30 a minute per user by default) is set with vakanssi/intake/download_rate_limit. See the Hooks reference.
Who can see candidate data
Access is controlled by roles and capabilities, the same system WordPress uses for the rest of your site. Vakanssi adds three roles (Recruiter, Hiring Manager, and HR / Compliance) with different levels of access, and administrators can do everything. The full breakdown is in Roles & permissions, and the developer-level capability list is in Capabilities & roles.
Know the default: all hiring staff see all candidates
By default, anyone who can open the hiring screens (Recruiter, Hiring Manager, or HR) can view, move, and download the CV of every candidate, not only the ones they are working on. This is deliberate, and fine for a single trusted hiring team.
If you need each recruiter to see only candidates assigned to them, plus any still unassigned, turn on assignment scoping in Collaboration Pro feature, requires a Vakanssi Pro license (Settings, then Collaboration, then “Scope by assignment”). It is off by default. Administrators and HR / Compliance always see everyone. See Collaboration.
An interview invite includes the candidate’s name and email in the calendar file, so only people on your hiring team (Recruiter, Hiring Manager, HR / Compliance, or Administrator) can be added as an interviewer.
The interviewer picker in the drawer only ever lists those people, and the same check runs again on the server when an interview is scheduled, edited, or a candidate self-books a proposed time, so a direct API request can’t add anyone else either.
Protecting the application form
The public application form is a common target for bots, so it is defended out of the box:
- A hidden honeypot field, a time-trap (submissions that arrive impossibly fast are rejected), and per-visitor rate limiting all run automatically, with no setup.
- You can add a visible CAPTCHA challenge on top: Cloudflare Turnstile is free, and hCaptcha and reCAPTCHA v3 come with Advanced anti-spam Pro feature, requires a Vakanssi Pro license. See Receiving applications.
- A separate, higher cap limits how many rejected-submission states one visitor can create, so repeatedly resubmitting invalid data can’t be used to flood the site’s database. A genuine applicant fixing a few mistakes never comes close to it.
- Since v0.37.6, the form handler reads only a fixed list of named fields, not everything a request sends. Since v0.37.3, a cover letter is capped at 20,000 characters and a longer one is refused, the same way an over-long name or phone number is.
- Since v0.37.8, a null byte in an applicant’s details is stripped before anything is stored or emailed, and a password-protected job refuses public applications.
Behind the scenes, admin actions are protected against cross-site request forgery (nonces), and every REST API route enforces a permission check before it runs. See the REST API reference.
Privacy and compliance (GDPR, CCPA)
Vakanssi’s free Privacy & Data Retention module (renamed from Privacy & Compliance in v0.37.11) gives you the tools that a GDPR or CCPA process relies on.
To be clear about what that means: the plugin does not make you compliant on its own. Compliance depends on your policies, your lawful basis for hiring, and how you actually run the process. What Vakanssi does is cover the mechanics, so the tooling is not what stands in your way.
Here is how the pieces map to the obligations you are likely thinking of:
- A lawful basis, captured (consent). Applicants must tick a consent box, linked to your privacy policy once that page is published, before they can submit, and that agreement is stored with their application. This gives you a recorded basis and supports the GDPR accountability principle.
- Right of access and right to erasure (GDPR Articles 15 and 17). Because Vakanssi plugs into WordPress core privacy, a candidate’s request is handled from Tools, then Export Personal Data or Erase Personal Data, matched by email. Erasing a candidate removes their applications, CV files, notes, interviews, evaluations, talent-pool tags (Pro), and email history together. The full walkthrough is in Privacy & GDPR.
- Storage limitation. A daily background job (the retention purge) anonymises a candidate’s personal details once their data passes its retention window: it deletes their CV files plus the personal data in linked records (notes, the sent-email log, interviews, and Scorecard Pro feature, requires a Vakanssi Pro license evaluations), so you are not holding CVs or contact details forever.
- The windows default to 24 months when consent was given and 6 months when it was not, are configurable under Hiring, then Settings, then General (1 to 120 months, each set separately), and a developer can also change them in code with the
vakanssi/retention/monthsfilter. Anonymised rows are kept so your hiring stats stay intact.
- The windows default to 24 months when consent was given and 6 months when it was not, are configurable under Hiring, then Settings, then General (1 to 120 months, each set separately), and a developer can also change them in code with the
- Accountability (an audit trail). An audit log records sensitive actions such as CV downloads, deletions, erasures, and anonymisations. The IP address it stores is anonymised by default; a developer can opt into full addresses with the
vakanssi/audit/anonymize_ipfilter. (Export requests themselves are tracked by WordPress core, on the Export Personal Data screen.) - Data residency. Self-hosting means candidate data stays wherever your site is hosted, in the region you chose. There is no separate cloud processor to add to your records of processing or your data-processing agreements.
The export and the erasure cover the same ground by design: the plugin’s exporter is built to include every category the eraser removes, so an access request never understates what you hold.
An export returns the candidate’s full profile (name, email, phone, location, LinkedIn, and any recruiter note), their talent-pool tags (Pro), and, per application, the cover letter, screening-question answers (Pro), notes, interviews, sent emails, and Scorecard evaluations (Pro), with each retained CV enclosed in the downloaded archive itself, not just named in the listing.
Sending events to other tools
If you use Integrations Pro feature, requires a Vakanssi Pro license to forward hiring events to Slack, Microsoft Teams, or a webhook, each plain webhook delivery is signed with a per-endpoint secret (the X-Vakanssi-Signature header) so the receiving service can verify it genuinely came from your site. Requests to private or internal addresses are refused. A delivery log with automatic retries lets you confirm what was sent.
The candidate data in a delivery’s logged payload is kept only while it may still be sent: a successful delivery stores none, and a failed one keeps it for 24 hours so you can Resend it, after which the daily purge clears it. A delivery still waiting after a week is given up on and cleared too. See Integrations.
What happens when you remove the plugin
- Deactivating the plugin changes nothing about your data.
- Uninstalling removes Vakanssi’s roles, capabilities, and settings, but keeps your hiring data unless you first enable Settings, then “Delete all data on uninstall”.
- If you do enable that full purge, Vakanssi deletes its tables and securely removes the protected CV folder. Guards confirm it owns the folder, refuse to follow symbolic links out of it, and refuse outright to delete the site’s own folders (the WordPress folder and its parent,
wp-content, uploads, or the plugin folder). - Your job posts are normal WordPress content and stay, but the purge removes their Vakanssi job details (salary, location, closing date, notification address). The careers page the setup wizard created goes to the Trash, where you can restore it; pages you made yourself are untouched. See Backups & data.
Secure by design
Vakanssi is built to defend the vulnerability classes that most affect WordPress plugins: SQL injection, broken access control (capability plus object-level checks on every sensitive action), cross-site scripting, insecure file upload and download (covered above), and cross-site request forgery (nonces on admin actions).
Every database query binds its values through prepared statements, and since v0.37.5 its table and column names too, rather than writing them into the query text. Output is escaped when printed, and markup that other code can filter in (such as a careers listing row) passes through WordPress’s HTML filter first, so it cannot add scripts.
The codebase has been through internal security review with those risks in mind. No software can promise perfect security, so keep WordPress, your theme, and your plugins updated, and use strong accounts for your hiring team.
Reporting a security issue
If you find a vulnerability, email security@vakanssi.com rather than posting in the support forum or on social media, so a fix can ship before the details are public. Include the Vakanssi version, your WordPress and PHP versions, and steps to reproduce, ideally against a clean install.
You will get an acknowledgement within three business days, and an initial assessment, with a severity and a rough timeline, within a week. Only the most recent release receives security fixes, since Vakanssi ships small, frequent updates with no maintenance branches, so updating is always part of the fix.
You will be credited in the changelog for the release that fixes it, unless you would rather not be. Please test only against sites you own or have written permission to test, and do not access, modify, or exfiltrate other people’s candidate data while investigating.