Custom ID vs UUID for Documents
I am trying to build a document submission portal in Django for a capstone project. I am trying to create a model for the set of documents needed to be uploaded by a user (a faculty member in my case).
My concern is whether I should use auto-generated custom IDs or UUIDs for the ID of every document. One thing I know about UUIDs is that they take up more space and can reduce performance, but one thing I consider is security, knowing that the documents that will be uploaded by users contain sensitive data.
However, I need to accommodate around 10-15 documents per user. I am worried that using UUIDs might take up a lot of space, and I also want to prioritize performance. This is why I came up with using just a custom ID for every document.
Concerned factors:
Performance
Storage Space
Security
Our scope is grounded in a single institution only and does not concern scalability
The size of a standard UUID is 128 bits. That is 16 bytes per UUID. You can fit 256 of them into a typical disk block (4096 bytes).
Concerned factors:
- Performance
The time to compare 2 UUIDs is trivial. The time to generate a UUID can be non-trivial, but if you only do it 10 to 15 times per user, it should be insignificant.
- Storage Space
You can store 256 UUIDs in a single disk block. There will be other overheads (e.g. database indexes) but even so, the per user overhead of UUIDs vs custom IDs in your use-case should be insignificant.
- Security
UUIDs are not more or less secure per se. Certain kinds (e.g. type 4) of UUID will be harder to guess than a (shorter) custom IDs. So that means that attacks that rely solely on guessing identifiers will be harder to carry out successfully with UUIDs.
However, a security scheme that relies solely on attackers not being to guess identifiers is inherently weak. (It depends on there not being any other way to get hold of the identifier for a target resource.)
- Our scope is grounded in a single institution only and does not concern scalability.
Not withstanding that ... you can have 2^128 distinct UUIDs (depending on type), though you will run into problems with performance is the number of extant UUIDs is too large. Custom IDs can in theory scale indefinitely, depending on how you generate and represent them.
For your case, use Django's normal integer primary key internally and a UUID as a separate public identifier.
Use id for internal database relationships and public_id in URLs/APIs:
/documents/550e8400-e29b-41d4-a716-446655440000/
rather than:
/documents/123/
Why?
| Concern | Answer |
|---|---|
| Performance | Integer PKs are smaller and efficient for joins/indexes. |
| Storage | UUIDs use more space, but with only 10–15 documents per user, the difference is negligible. The uploaded files themselves will consume far more storage. |
| Security | UUIDs make IDs difficult to guess/enumerate, but UUIDs are not authorization. |
| Scalability | Not relevant enough at your single-institution scale to influence this decision. |
| Custom IDs | Avoid sequential IDs such as DOC-001, DOC-002; they are still predictable and add unnecessary custom logic. |
Most importantly, always verify that the requested document belongs to the currently authenticated user before allowing them to view, download, modify, or delete it.
A UUID prevents someone from simply trying /documents/1/, /documents/2/, etc., but if they somehow obtain another user's UUID, your application must still deny access. OWASP recommends exactly this approach: unpredictable identifiers can be used as defense in depth, but proper server-side authorization is the actual security control.
Also keep the uploaded files in private storage rather than exposing them through unrestricted public media URLs.
Conclusion
For your capstone:
Integer PK internally + UUID publicly + ownership/authorization checks + private file storage.
At your scale, UUID storage/performance overhead is insignificant. Don't build a custom ID system just to save a few bytes, and don't rely on UUIDs alone for security.