Hello,
We have web hosting, the old « PERSO » plan.
Is it possible to create private directories?
Instead of leaving the entire space accessible,
limit directories to users in, for example, wp-contents/uploads.
Please let me know if this is possible (I haven't found the command).
or if it's a plan‑level issue.
Thank you for your time,
José
Hello, the answer to your question is: no (if you access via FTP)
However, the wp-content/uploads folder is not meant to be manipulated outside of WordPress (because every file in /uploads/... is known in the database). You will create inconsistencies if you add or delete files without going through WordPress.
If you only want to view them, it would be better to use a plugin. That should exist and I haven't looked for it.
Alright and thanks @fritz2cat
I wonder where I could host this little app!!!
TTY
June 1, 2026, 2:06pm
4
Hello,
I’m wondering where I could host this little application!!!
Can you explain the need more precisely?
Logically (PHP plugins), you can restrict access, but not at the file‑system/SFTP level, etc. That’s what @fritz2cat meant, I think.
No, it’s very simple, it’s a quiz. An HTML site, a CSS file, a JS, a bit of Python…
It’s 500 KB
I was thinking of putting this on a Gillab page?
Hi @ZERO_DECHETT ,
If it’s a small site on its own, create a directory outside of your wp-content/uploads and even outside of your WordPress (so not in /www, but rather next to it), and in "multisite" you create a new sub‑domain name, for example quiz.example.com , linked to that root folder.
It’s tidy, and that way there won’t be any interference with your WordPress.
Thanks for the response @fritz2cat
I have two little questions:
My directory tree looks like this (created before I arrived)
I'm wondering if the directories z, e, r, zerodechzi are necessary?!
So I'll test with the directory in home
How can I make this directory—and only this directory—accessible to the developer?
Without giving them access to the whole site… and at the same time keeping their app accessible?!
Thanks for your time, José
Hello @ZERO_DECHETT
Create a subdomain: demo.domaine.com
And in the multisite associate: demo.domaine.com with the demo folder
in the same way as: domaine.com is associated with the www folder
You can also protect access to this folder for this single user with: /demo/.htpasswd
Gaston
June 4, 2026, 7:28am
10
Hello @ZERO_DECHETT
I forgot: You will need to create a dedicated database for this demo folder.
If you use the same database for domaine.com and demo.domaine.com your user will be able to see all the content of the domaine.com site's database.
Actually, that’s what I just did:
with videos, and it works fine, and access is simple
with a little HTML code… which works on my computer but not online!!!
(it displays, but the automations don’t work !!!)
Gaston
June 4, 2026, 8:12am
13
Ha! Your automation code.
I saw. I was on an absolute path with the uploads path...
I switched back to relative!!!
Thanks
It works.
My test code: https://zerodechettroyes.org/quiz-test :).
However… how can we make the developer access only his directory without giving full FTP access.
We have the PERSO plan and OVH does not give us the possibility to create an additional FTP account.
How can we make the app accessible to everyone,
and have the code directory accessible only to a few dedicated people
and make it simple to set up!!!
The code is, in the domain's WP, in www/quizz
Currently, I’m looking at a solution with ftpaccess.
Does OVH allow this with the PERSO plan?
Thank you for your time.
Kind regards, José
Gaston
June 5, 2026, 8:57am
16
Hello @ZERO_DECHETT
It's impossible. FTP access necessarily covers the entire PERSO hosting space.
Hello,
This is not available. All files of a single hosting belong to one and the same Unix user. That user is you, with your FTP login, and you therefore have rights to all files that are in this hosting.
You can look at solutions like this https://www.tldevtech.com/best-php-web-based-file-manager/
or other “php file manager” tools, but be careful, they can contain serious security holes if badly programmed, and you need to find one that limits you to the folder you want to make available to your developer. (chroot in Unix jargon)
All of this is a makeshift solution; the proper fix would be a separate hosting for your quiz (for example a Starter)
Or a Pro plan that offers “chrooted” FTP users (but I don’t have a Pro hosting and I have no experience in that matter)
I asked Mistral AI for a solution.
It suggested a PHP solution: an interface to manage files and sub‑directories , with privileged access.
(for info, I use FileZilla)
<?php
// Code developed by Mistral AI
// Absolute path of the allowed subdirectory
$allowed_dir = '/home/votre_utilisateur/www/mon-sous-repertoire';
// Credential verification (to adapt)
$valid_users = [
'utilisateur1' => 'motdepasse1',
'utilisateur2' => 'motdepasse2',
];
// Authentication
if (!isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) ||
!isset($valid_users[$_SERVER['PHP_AUTH_USER']]) ||
$valid_users[$_SERVER['PHP_AUTH_USER']] !== $_SERVER['PHP_AUTH_PW']) {
header('WWW-Authenticate: Basic realm="Accès restreint"');
header('HTTP/1.0 401 Unauthorized');
die('Accès refusé.');
}
// Current path management (for navigation)
$current_dir = $allowed_dir;
if (isset($_GET['dir']) && !empty($_GET['dir'])) {
$requested_dir = $allowed_dir . '/' . ltrim($_GET['dir'], '/');
if (strpos($requested_dir, $allowed_dir) === 0 && is_dir($requested_dir)) {
$current_dir = $requested_dir;
}
}
// --- Action handling ---
// 1. File upload
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
$upload_dir = $current_dir . '/';
$upload_file = $upload_dir . basename($_FILES['file']['name']);
if (move_uploaded_file($_FILES['file']['tmp_name'], $upload_file)) {
echo "<p style='color: green;'>✅ Fichier uploadé : " . htmlspecialchars($_FILES['file']['name']) . "</p>";
} else {
echo "<p style='color: red;'>❌ Erreur lors de l'upload.</p>";
}
}
// 2. File deletion
if (isset($_GET['delete_file'])) {
$file_path = $allowed_dir . '/' . ltrim($_GET['delete_file'], '/');
if (file_exists($file_path) && strpos($file_path, $allowed_dir) === 0 && is_file($file_path)) {
unlink($file_path);
echo "<p style='color: green;'>✅ Fichier supprimé : " . htmlspecialchars(basename($file_path)) . "</p>";
}
}
// 3. Directory creation
if (isset($_POST['new_dir']) && !empty($_POST['new_dir'])) {
$new_dir_path = $current_dir . '/' . trim($_POST['new_dir'], '/');
if (!file_exists($new_dir_path) && strpos($new_dir_path, $allowed_dir) === 0) {
if (mkdir($new_dir_path, 0755, true)) {
echo "<p style='color: green;'>✅ Répertoire créé : " . htmlspecialchars($_POST['new_dir']) . "</p>";
} else {
echo "<p style='color: red;'>❌ Erreur lors de la création du répertoire.</p>";
}
} else {
echo "<p style='color: red;'>❌ Le répertoire existe déjà ou le chemin est invalide.</p>";
}
}
// 4. Directory deletion (only if empty)
if (isset($_GET['delete_dir'])) {
$dir_path = $allowed_dir . '/' . ltrim($_GET['delete_dir'], '/');
if (is_dir($dir_path) && strpos($dir_path, $allowed_dir) === 0) {
if (rmdir($dir_path)) {
echo "<p style='color: green;'>✅ Répertoire supprimé : " . htmlspecialchars(basename($dir_path)) . "</p>";
} else {
echo "<p style='color: red;'>❌ Le répertoire n'est pas vide ou une erreur est survenue.</p>";
}
}
}
// --- Interface display ---
// Show current path
echo "<h2>Chemin : <code>" . str_replace($allowed_dir, '', $current_dir) . "</code></h2>";
// Link to go up one level
if ($current_dir !== $allowed_dir) {
$parent_dir = dirname($current_dir);
$relative_parent = str_replace($allowed_dir, '', $parent_dir);
echo "<p><a href='?dir=" . urlencode(ltrim($relative_parent, '/')) . "'>⬆️ Remonter d'un niveau</a></p>";
}
// Show files and folders
$files = scandir($current_dir);
echo "<h3>Contenu du répertoire :</h3>";
echo "<ul>";
foreach ($files as $file) {
if ($file === '.' || $file === '..') continue;
$path = $current_dir . '/' . $file;
$relative_path = str_replace($allowed_dir, '', $path);
if (is_dir($path)) {
echo "<li>
<strong><a href='?dir=" . urlencode(ltrim($relative_path, '/')) . "'>📁 $file/</a></strong>
<a href='?delete_dir=" . urlencode(ltrim($relative_path, '/')) . "' style='color: red;' onclick='return confirm(\"Supprimer ce répertoire (doit être vide) ?\")'>❌</a>
</li>";
} else {
echo "<li>
<a href='?download=" . urlencode(ltrim($relative_path, '/')) . "'>📄 $file</a>
<a href='?delete_file=" . urlencode(ltrim($relative_path, '/')) . "' style='color: red;' onclick='return confirm(\"Supprimer ce fichier ?\")'>❌</a>
</li>";
}
}
echo "</ul>";
// File download
if (isset($_GET['download'])) {
$file_path = $allowed_dir . '/' . ltrim($_GET['download'], '/');
if (file_exists($file_path) && strpos($file_path, $allowed_dir) === 0) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file_path) . '"');
readfile($file_path);
exit;
}
}
// --- Forms ---
echo "<hr>";
// File upload form
echo "<h3>📤 Uploader un fichier</h3>";
echo "<form method='post' enctype='multipart/form-data'>";
echo "<input type='file' name='file' required>";
echo "<button type='submit'>Uploader</button>";
echo "</form>";
// Directory creation form
echo "<h3>📁 Créer un répertoire</h3>";
echo "<form method='post'>";
echo "<input type='text' name='new_dir' placeholder='Nom du répertoire' required>";
echo "<button type='submit'>Créer</button>";
echo "</form>";
// Help message
echo "<p><em>Note : Les répertoires ne peuvent être supprimés que s'ils sont vides.</em></p>";
?>
I tested it on our OVH hosting, and it works fine.
And it suits our problem well.
Kind regards, José
Gaston
June 5, 2026, 2:34pm
19
Hello @ZERO_DECHETT
I did all this back in 2009 for a site, but at the time I didn't have the masterful help of Mistral AI.
I admit that 17 years later I don't really remember the code
Gaston
June 5, 2026, 3:34pm
20
And no use of FTP or SFTP via FileZilla.