diff --git a/compendium-frontend/src/survey/management/SurveyManagementComponent.tsx b/compendium-frontend/src/survey/management/SurveyManagementComponent.tsx
index 8f1138f96e4558839b76d7deb00e498e82c2b34a..d1fedda1b4e940ebd6657d1469f678bf76a41961 100644
--- a/compendium-frontend/src/survey/management/SurveyManagementComponent.tsx
+++ b/compendium-frontend/src/survey/management/SurveyManagementComponent.tsx
@@ -13,6 +13,7 @@ import { Spinner } from "react-bootstrap";
 import StatusButton from "../StatusButton";
 import { Link } from "react-router-dom";
 import { debounce } from "lodash"
+import { validateSurvey } from "../utils";
 
 function updateSurveyNotes(year: number, nren_id: number, notes: string) {
     fetch('/api/survey/' + year + '/' + nren_id + '/notes', {
@@ -218,6 +219,9 @@ function SurveyManagementComponent() {
                                                                 title="Remove the lock from the survey so that another person can open the survey for editing. WARNING: The person that currently has the lock will not be able to save their changes anymore once someone else starts editing!">
                                                                 remove lock
                                                             </Button>
+                                                            <Button onClick={() => validateSurvey(survey.year, response.nren.name).then(console.log)} style={{ pointerEvents: 'auto' }}>
+                                                                Validate Survey
+                                                            </Button>
                                                         </td>
                                                     </tr>
                                                 ))}
diff --git a/compendium-frontend/src/survey/utils.ts b/compendium-frontend/src/survey/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a713a8a2750aa11bb7a33b5a46a39f18e90635f9
--- /dev/null
+++ b/compendium-frontend/src/survey/utils.ts
@@ -0,0 +1,60 @@
+import { validateQuestion, oldValidateWebsiteUrl } from './validation/validation';
+
+export async function validateSurvey(year, nren) {
+    const { Model, FunctionFactory, Serializer } = await import('survey-core');
+
+    function setupProperties() {
+        const descriptionProperty = Serializer.getAllPropertiesByName("customDescription");
+        const hideCheckboxLabelsProperty = Serializer.getAllPropertiesByName("hideCheckboxLabels");
+
+        if (!descriptionProperty.length) {
+            Serializer.addProperty("itemvalue", "customDescription:text");
+        }
+        if (!hideCheckboxLabelsProperty.length) {
+            Serializer.addProperty("question", "hideCheckboxLabels:boolean");
+        }
+    }
+
+    if (!FunctionFactory.Instance.hasFunction("validateQuestion")) {
+        FunctionFactory.Instance.register("validateQuestion", validateQuestion);
+    }
+
+    if (!FunctionFactory.Instance.hasFunction("validateWebsiteUrl")) {
+        FunctionFactory.Instance.register("validateWebsiteUrl", oldValidateWebsiteUrl);
+    }
+
+    if (!year || !nren) {
+        return true;
+    }
+    const url = `/api/response/load/${year}/${nren}`;
+
+    const response = await fetch(url);
+    const json = await response.json();
+
+    if (!response.ok) {
+
+        if ('message' in json) {
+            throw new Error(json.message);
+        } else {
+            throw new Error(`Request failed with status ${response.status}`);
+        }
+    }
+
+    // Setup custom properties that allow the survey to work
+    setupProperties();
+
+    const survey = new Model(json['model']);
+    survey.setVariable('surveyyear', year);
+    survey.setVariable('previousyear', parseInt(year!) - 1);
+
+    survey.showNavigationButtons = false;
+    survey.requiredText = '';
+
+    survey.data = json['data'];
+    survey.clearIncorrectValues(true);
+
+    const valid = survey.validate.bind(survey, true, true)();
+
+    console.log(survey)
+    return valid;
+}
\ No newline at end of file
diff --git a/compendium-frontend/src/survey/validation/validation.ts b/compendium-frontend/src/survey/validation/validation.ts
index e0138982678715e62b816bb868d6eb6432192531..f968223b7d77494d972b770e868f2f8fe19a29bb 100644
--- a/compendium-frontend/src/survey/validation/validation.ts
+++ b/compendium-frontend/src/survey/validation/validation.ts
@@ -1,3 +1,9 @@
+interface ValidationQuestion {
+    name?: string;
+    value?: string | number | null;
+    data?: ValidationQuestion;
+}
+
 function validateWebsiteUrl(value, nonEmpty = false) {
     if (!nonEmpty && (value == undefined || value == null || value == '')) {
         return true;
@@ -17,6 +23,67 @@ function validateWebsiteUrl(value, nonEmpty = false) {
         return false;
     }
 }
+
+// Overrides for questions that need to be validated differently from the default expression in their group
+const questionOverrides = {
+    data_protection_contact: (..._args) => true, // don't validate the contact field, anything goes..
+}
+
+export function validateQuestion(this: { question: ValidationQuestion, row?}, params) {
+    try {
+        const question = this.question;
+        const validator = params[0] || undefined;
+
+        const matrix = question.data && 'name' in question.data;
+
+        let questionName;
+
+        if (matrix) {
+            questionName = question.data!.name;
+        } else {
+            questionName = question.name;
+        }
+        const value = question.value
+
+        const hasOverride = questionOverrides[questionName];
+        if (hasOverride) {
+            return hasOverride(value, ...params.slice(1));
+        }
+
+        const validationFunction = validationFunctions[validator];
+        if (!validationFunction) {
+            throw new Error(`Validation function ${validator} not found for question ${questionName}`);
+        }
+
+        return validationFunction(value, ...params.slice(1));
+    } catch (e) {
+        console.error(e);
+        return false;
+    }
+}
+
+export function oldValidateWebsiteUrl(params) {
+    let value = params[0];
+    if ((value == undefined || value == null || value == '')) {
+        return true;
+    }
+    try {
+        value = value.trim();
+        if (value.includes(" ")) {
+            return false;
+        }
+        // if there's not a protocol, add one for the test
+        if (!value.includes(":/")) {
+            value = "https://" + value;
+        }
+
+        const url = new URL(value);
+        return !!url
+    } catch {
+        return false;
+    }
+}
+
 export const validationFunctions = {
     validateWebsiteUrl,
 }
\ No newline at end of file