diff --git a/docs/releases.md b/docs/releases.md
index 85b33a13..91d32dfd 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -24,8 +24,9 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
 
 **Bugs:**
 
-* Web app: English language strings fixes ([#203](https://github.com/binwiederhier/ntfy/issues/203), thanks to [@StoyanDimitrov](https://github.com/StoyanDimitrov))
+* Web app: English language strings fixes, additional descriptions for settings ([#203](https://github.com/binwiederhier/ntfy/issues/203), thanks to [@StoyanDimitrov](https://github.com/StoyanDimitrov))
 * Web app: Show error message snackbar when sending test notification fails ([#205](https://github.com/binwiederhier/ntfy/issues/205), thanks to [@cmeis](https://github.com/cmeis))
+* Web app: basic URL validation in user management ([#204](https://github.com/binwiederhier/ntfy/issues/204), thanks to [@cmeis](https://github.com/cmeis))
 
 **Translations (web app):**
 
diff --git a/web/public/static/langs/en.json b/web/public/static/langs/en.json
index a2dca266..a27ff867 100644
--- a/web/public/static/langs/en.json
+++ b/web/public/static/langs/en.json
@@ -33,7 +33,7 @@
   "notifications_none_for_any_title": "You haven't received any notifications.",
   "notifications_none_for_any_description": "To send notifications to a topic, simply PUT or POST to the topic URL. Here's an example using one of your topics.",
   "notifications_no_subscriptions_title": "It looks like you don't have any subscriptions yet.",
-  "notifications_no_subscriptions_description": "Click the \"Add subscription\" link to create or subscribe to a topic. After that, you can send messages via PUT or POST and you'll receive notifications here.",
+  "notifications_no_subscriptions_description": "Click the \"{{linktext}}\" link to create or subscribe to a topic. After that, you can send messages via PUT or POST and you'll receive notifications here.",
   "notifications_example": "Example",
   "notifications_more_details": "For more information, check out the <websiteLink>website</websiteLink> or <docsLink>documentation</docsLink>.",
   "notifications_loading": "Loading notifications …",
@@ -103,8 +103,13 @@
   "subscribe_dialog_error_user_anonymous": "anonymous",
   "prefs_notifications_title": "Notifications",
   "prefs_notifications_sound_title": "Notification sound",
+  "prefs_notifications_sound_description_none": "Notifications do not play any sound when they arrive",
+  "prefs_notifications_sound_description_some": "Notifications play the {{sound}} sound when they arrive",
   "prefs_notifications_sound_no_sound": "No sound",
   "prefs_notifications_min_priority_title": "Minimum priority",
+  "prefs_notifications_min_priority_description_any": "Showing all notifications, regardless of priority",
+  "prefs_notifications_min_priority_description_x_or_higher": "Show notifications if priority is {{number}} ({{name}}) or above",
+  "prefs_notifications_min_priority_description_max": "Show notifications if priority is 5 (max)",
   "prefs_notifications_min_priority_any": "Any priority",
   "prefs_notifications_min_priority_low_and_higher": "Low priority and higher",
   "prefs_notifications_min_priority_default_and_higher": "Default priority and higher",
@@ -116,6 +121,11 @@
   "prefs_notifications_delete_after_one_day": "After one day",
   "prefs_notifications_delete_after_one_week": "After one week",
   "prefs_notifications_delete_after_one_month": "After one month",
+  "prefs_notifications_delete_after_never_description": "Notifications are never auto-deleted",
+  "prefs_notifications_delete_after_three_hours_description": "Notifications are auto-deleted after three hours",
+  "prefs_notifications_delete_after_one_day_description": "Notifications are auto-deleted after one day",
+  "prefs_notifications_delete_after_one_week_description": "Notifications are auto-deleted after one week",
+  "prefs_notifications_delete_after_one_month_description": "Notifications are auto-deleted after one month",
   "prefs_users_title": "Manage users",
   "prefs_users_description": "Add/remove users for your protected topics here. Please note that username and password are stored in the browser's local storage.",
   "prefs_users_add_button": "Add user",
@@ -131,6 +141,11 @@
   "prefs_users_dialog_button_save": "Save",
   "prefs_appearance_title": "Appearance",
   "prefs_appearance_language_title": "Language",
+  "priority_min": "min",
+  "priority_low": "low",
+  "priority_default": "default",
+  "priority_high": "high",
+  "priority_max": "max",
   "error_boundary_title": "Oh no, ntfy crashed",
   "error_boundary_description": "This should obviously not happen. Very sorry about this.<br/>If you have a minute, please <githubLink>report this on GitHub</githubLink>, or let us know via <discordLink>Discord</discordLink> or <matrixLink>Matrix</matrixLink>.",
   "error_boundary_button_copy_stack_trace": "Copy stack trace",
diff --git a/web/src/app/utils.js b/web/src/app/utils.js
index 2f04dfd8..adba2e9f 100644
--- a/web/src/app/utils.js
+++ b/web/src/app/utils.js
@@ -24,7 +24,7 @@ export const expandUrl = (url) => [`https://${url}`, `http://${url}`];
 export const expandSecureUrl = (url) => `https://${url}`;
 
 export const validUrl = (url) => {
-    return url.match(/^https?:\/\//);
+    return url.match(/^https?:\/\/.+/);
 }
 
 export const validTopic = (topic) => {
@@ -153,17 +153,38 @@ export const openUrl = (url) => {
 };
 
 export const sounds = {
-    "beep": beep,
-    "juntos": juntos,
-    "pristine": pristine,
-    "ding": ding,
-    "dadum": dadum,
-    "pop": pop,
-    "pop-swoosh": popSwoosh
+    "ding": {
+        file: ding,
+        label: "Ding"
+    },
+    "juntos": {
+        file: juntos,
+        label: "Juntos"
+    },
+    "pristine": {
+        file: pristine,
+        label: "Pristine"
+    },
+    "dadum": {
+        file: dadum,
+        label: "Dadum"
+    },
+    "pop": {
+        file: pop,
+        label: "Pop"
+    },
+    "pop-swoosh": {
+        file: popSwoosh,
+        label: "Pop swoosh"
+    },
+    "beep": {
+        file: beep,
+        label: "Beep"
+    }
 };
 
-export const playSound = async (sound) => {
-    const audio = new Audio(sounds[sound]);
+export const playSound = async (id) => {
+    const audio = new Audio(sounds[id].file);
     return audio.play();
 };
 
diff --git a/web/src/components/Notifications.js b/web/src/components/Notifications.js
index eb338803..a412e29e 100644
--- a/web/src/components/Notifications.js
+++ b/web/src/components/Notifications.js
@@ -389,7 +389,9 @@ const NoSubscriptions = () => {
                 {t("notifications_no_subscriptions_title")}
             </Typography>
             <Paragraph>
-                {t("notifications_no_subscriptions_description")}
+                {t("notifications_no_subscriptions_description", {
+                    linktext: t("nav_button_subscribe")
+                })}
             </Paragraph>
             <Paragraph>
                 <ForMoreDetails/>
diff --git a/web/src/components/Preferences.js b/web/src/components/Preferences.js
index e6f28194..11b688e5 100644
--- a/web/src/components/Preferences.js
+++ b/web/src/components/Preferences.js
@@ -32,8 +32,13 @@ import DialogTitle from "@mui/material/DialogTitle";
 import DialogContent from "@mui/material/DialogContent";
 import DialogActions from "@mui/material/DialogActions";
 import userManager from "../app/UserManager";
-import {playSound, shuffle} from "../app/utils";
+import {playSound, shuffle, sounds, validUrl} from "../app/utils";
 import {useTranslation} from "react-i18next";
+import priority1 from "../img/priority-1.svg";
+import priority2 from "../img/priority-2.svg";
+import priority3 from "../img/priority-3.svg";
+import priority4 from "../img/priority-4.svg";
+import priority5 from "../img/priority-5.svg";
 
 const Preferences = () => {
     return (
@@ -51,7 +56,7 @@ const Notifications = () => {
     const { t } = useTranslation();
     return (
         <Card sx={{p: 3}}>
-            <Typography variant="h5">
+            <Typography variant="h5" sx={{marginBottom: 2}}>
                 {t("prefs_notifications_title")}
             </Typography>
             <PrefGroup>
@@ -72,19 +77,19 @@ const Sound = () => {
     if (!sound) {
         return null; // While loading
     }
+    let description;
+    if (sound === "none") {
+        description = t("prefs_notifications_sound_description_none");
+    } else {
+        description = t("prefs_notifications_sound_description_some", { sound: sounds[sound].label });
+    }
     return (
-        <Pref title={t("prefs_notifications_sound_title")}>
+        <Pref title={t("prefs_notifications_sound_title")} description={description}>
             <div style={{ display: 'flex', width: '100%' }}>
                 <FormControl fullWidth variant="standard" sx={{ margin: 1 }}>
                     <Select value={sound} onChange={handleChange}>
                         <MenuItem value={"none"}>{t("prefs_notifications_sound_no_sound")}</MenuItem>
-                        <MenuItem value={"ding"}>Ding</MenuItem>
-                        <MenuItem value={"juntos"}>Juntos</MenuItem>
-                        <MenuItem value={"pristine"}>Pristine</MenuItem>
-                        <MenuItem value={"dadum"}>Dadum</MenuItem>
-                        <MenuItem value={"pop"}>Pop</MenuItem>
-                        <MenuItem value={"pop-swoosh"}>Pop swoosh</MenuItem>
-                        <MenuItem value={"beep"}>Beep</MenuItem>
+                        {Object.entries(sounds).map(s => <MenuItem key={s[0]} value={s[0]}>{s[1].label}</MenuItem>)}
                     </Select>
                 </FormControl>
                 <IconButton onClick={() => playSound(sound)} disabled={sound === "none"}>
@@ -104,8 +109,26 @@ const MinPriority = () => {
     if (!minPriority) {
         return null; // While loading
     }
+    const priorities = {
+        1: t("priority_min"),
+        2: t("priority_low"),
+        3: t("priority_default"),
+        4: t("priority_high"),
+        5: t("priority_max")
+    }
+    let description;
+    if (minPriority === 1) {
+        description = t("prefs_notifications_min_priority_description_any");
+    } else if (minPriority === 5) {
+        description = t("prefs_notifications_min_priority_description_max");
+    } else {
+        description = t("prefs_notifications_min_priority_description_x_or_higher", {
+            number: minPriority,
+            name: priorities[minPriority]
+        });
+    }
     return (
-        <Pref title={t("prefs_notifications_min_priority_title")}>
+        <Pref title={t("prefs_notifications_min_priority_title")} description={description}>
             <FormControl fullWidth variant="standard" sx={{ m: 1 }}>
                 <Select value={minPriority} onChange={handleChange}>
                     <MenuItem value={1}>{t("prefs_notifications_min_priority_any")}</MenuItem>
@@ -125,11 +148,20 @@ const DeleteAfter = () => {
     const handleChange = async (ev) => {
         await prefs.setDeleteAfter(ev.target.value);
     }
-    if (!deleteAfter) {
+    if (deleteAfter === null || deleteAfter === undefined) { // !deleteAfter will not work with "0"
         return null; // While loading
     }
+    const description = (() => {
+        switch (deleteAfter) {
+            case 0: return t("prefs_notifications_delete_after_never_description");
+            case 10800: return t("prefs_notifications_delete_after_three_hours_description");
+            case 86400: return t("prefs_notifications_delete_after_one_day_description");
+            case 604800: return t("prefs_notifications_delete_after_one_week_description");
+            case 2592000: return t("prefs_notifications_delete_after_one_month_description");
+        }
+    })();
     return (
-        <Pref title={t("prefs_notifications_delete_after_title")}>
+        <Pref title={t("prefs_notifications_delete_after_title")} description={description}>
             <FormControl fullWidth variant="standard" sx={{ m: 1 }}>
                 <Select value={deleteAfter} onChange={handleChange}>
                     <MenuItem value={0}>{t("prefs_notifications_delete_after_never")}</MenuItem>
@@ -145,10 +177,7 @@ const DeleteAfter = () => {
 
 const PrefGroup = (props) => {
     return (
-        <div style={{
-            display: 'flex',
-            flexWrap: 'wrap'
-        }}>
+        <div>
             {props.children}
         </div>
     )
@@ -156,26 +185,31 @@ const PrefGroup = (props) => {
 
 const Pref = (props) => {
     return (
-        <>
+        <div style={{
+            display: "flex",
+            flexDirection: "row",
+            marginTop: "10px",
+            marginBottom: "20px",
+        }}>
             <div style={{
-                flex: '1 0 30%',
-                display: 'inline-flex',
+                flex: '1 0 40%',
+                display: 'flex',
                 flexDirection: 'column',
-                minHeight: '60px',
-                justifyContent: 'center'
+                justifyContent: 'center',
+                paddingRight: '30px'
             }}>
-                <b>{props.title}</b>
+                <div><b>{props.title}</b></div>
+                {props.description && <div><em>{props.description}</em></div>}
             </div>
             <div style={{
-                flex: '1 0 calc(70% - 50px)',
-                display: 'inline-flex',
+                flex: '1 0 calc(60% - 50px)',
+                display: 'flex',
                 flexDirection: 'column',
-                minHeight: '60px',
                 justifyContent: 'center'
             }}>
                 {props.children}
             </div>
-        </>
+        </div>
     );
 };
 
@@ -202,8 +236,8 @@ const Users = () => {
     };
     return (
         <Card sx={{ padding: 1 }}>
-            <CardContent>
-                <Typography variant="h5">
+            <CardContent sx={{ paddingBottom: 1 }}>
+                <Typography variant="h5" sx={{marginBottom: 2}}>
                     {t("prefs_users_title")}
                 </Typography>
                 <Paragraph>
@@ -260,7 +294,7 @@ const UserTable = (props) => {
         <Table size="small">
             <TableHead>
                 <TableRow>
-                    <TableCell>{t("prefs_users_table_user_header")}</TableCell>
+                    <TableCell sx={{paddingLeft: 0}}>{t("prefs_users_table_user_header")}</TableCell>
                     <TableCell>{t("prefs_users_table_base_url_header")}</TableCell>
                     <TableCell/>
                 </TableRow>
@@ -271,7 +305,7 @@ const UserTable = (props) => {
                         key={user.baseUrl}
                         sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
                     >
-                        <TableCell component="th" scope="row">{user.username}</TableCell>
+                        <TableCell component="th" scope="row" sx={{paddingLeft: 0}}>{user.username}</TableCell>
                         <TableCell>{user.baseUrl}</TableCell>
                         <TableCell align="right">
                             <IconButton onClick={() => handleEditClick(user)}>
@@ -307,8 +341,12 @@ const UserDialog = (props) => {
         if (editMode) {
             return username.length > 0 && password.length > 0;
         }
+        const baseUrlValid = validUrl(baseUrl);
         const baseUrlExists = props.users?.map(user => user.baseUrl).includes(baseUrl);
-        return !baseUrlExists && username.length > 0 && password.length > 0;
+        return baseUrlValid
+            && !baseUrlExists
+            && username.length > 0
+            && password.length > 0;
     })();
     const handleSubmit = async () => {
         props.onSubmit({
@@ -373,7 +411,7 @@ const Appearance = () => {
     const { t } = useTranslation();
     return (
         <Card sx={{p: 3}}>
-            <Typography variant="h5">
+            <Typography variant="h5" sx={{marginBottom: 2}}>
                 {t("prefs_appearance_title")}
             </Typography>
             <PrefGroup>